참고
객체와 관계형 데이터베이스의 차이
NOTE
1.
상속
•
객체는 상속 관계가 있지만, 관계형 데이터베이스는 상속 관계가 없다
2.
연관관계
•
객체는 reference(참조)를 가지고 있다. (ex 연관된 객체를 getter로 가져올 수 있음)
•
관계형 데이터베이스는 PK나 FK로 join해서 필요한 데이터를 찾을 수 있다
3.
데이터 타입
4.
데이터 식별 방법
JPA
NOTE
Java Persistence API (자바 진영의 ORM 기술 표준)
ORM
•
Object-relational mapping(객체 관계 매핑)
•
객체는 객체대로 설계, 관계형 데이터베이스는 관계형 데이터베이스 대로 설계
•
ORM 프레임워크가 중간에서 매핑해줌
•
대중적인 언어에서 대부분 ORM기술이 존재한다
JPA 개념
NOTE
JPA는 어플리케이션과 JDBC 사이에서 동작한다
•
JAVA 어플리케이션에서 JPA에게 명령하면, JPA는 JDBC API를 사용해서 SQL을 만들어서 DB에 보낸다
JPA 사용이유
NOTE
•
SQL 중식적인 개발 → 객체 중심으로 개발
•
생산성
•
유지보수
•
패러다임 불일치 해결
•
성능
•
데이터 접근 추상화와 벤더 독립성
•
표준
생산성
•
CRUD가 다 정의되어 편하다
◦
저장 : jpa.persist(member)
◦
조회 : Member member = jpa.find(memberId)
◦
수정 : member.setName(”변경할 이름”)
◦
삭제 : jpa.remove(member)
유지보수
•
기존에는 필드 변경시 모든 SQL을 수정했어야 했다
◦
ex) 필드 1개가 추가되면, 그것을 사용하는 모든 곳을 수정해야 했음 → 실수 확률 증가
•
JPA를 사용하면 필드만 추가하면 된다!
◦
ex) db에 column이 추가되었다는 가정하에
JPA 동작
NOTE
저장
•
JPA에게 Member객체를 넘기면 JPA가 객체를 분석한다
•
INSERT query생성 (JPA가 query를 생성함!)
•
JPA가 JDBC API를 사용해서 INSERT query를 DB에 내보냄
•
패러다임 불일치 해결!
조회
•
JPA에게 PK값으로 요청한다
•
JPA는 SELECT query 생성
•
JDBC API를 통해 DB에 보내고 결과를 받음
•
ResultSet 매핑
•
패러다임 불일치 해결!
JPA와 패러다임 불일치 해결
NOTE
객체와 관계형 데이터베이스는 지향하는 목적이 다르므로 기능과 표현 방법이 다르다
JPA와 상속
NOTE
•
객체는 상속이 있지만 테이블은 상속이 없다
◦
객체 저장 → 객체를 분해해서 2개의 INSERT 쿼리 사용
◦
객체 조회 → 두 테이블을 join해서 조회한 정보로 객체를 생성
◦
이 과정이 모두 패러다임 불일치를 해결하기 위한 비용이다
•
JPA를 사용하면 단순히 자바 컬렉션에 저장하듯이 저장하면 된다!
◦
객체 저장
jpa.persist(album);
Java
복사
저장
◦
객체 조회
String albumId = "id100";
Album album = jpa.find(Album.class, albumId);
Java
복사
조회
JPA와 연관관계
NOTE
•
객체는 참조로 다른 객체와 관계를 맺는다
•
테이블은 외래 키를 사용해서 다른 테이블과 관계를 맺는다
•
테이블 중심 모델링 (외래키)
class Member {
String id; //MEMBER_ID 컬럼
Long teamId; //TEAM_ID 컬럼 (FK)
String username; //USERNAME 컬럼
}
class Team {
Long id; //TEAM_ID 컬럼 (PK)
String name; //NAME 컬럼
}
Java
복사
◦
장점 : 테이블에 저장하거나, 조회할 때 필드를 그대로 사용하기 때문에 편하다
◦
단점 : Member에서 Team으로 객체 참조가 없다
▪
member.getTeam()을 통한 객체 그래프 탐색이 불가능함
•
객체지향 모델링 (참조)
class Member {
String id; //MEMBER_ID 컬럼
Team team; //참조로 연관관계를 맺는다.
String username; //USERNAME 컬럼
Team getTeam() {...}
}
class Team {
Long id; //TEAM_ID 컬럼 (PK)
String name; //NAME 컬럼
}
Java
복사
◦
장점 :객체 참조가 있어 member.getTeam()을 통한 탐색이 가능함
◦
단점 : 객체를 저장하기 위해 SQL 변환 작업이 늘어난다.
•
JPA를 통한 연관관계 해결!
◦
JPA는 관계 맺고 있는 객체 참조를 외래 키로 변환해서 INSERT SQL 작성을 해주고, 조회할 때 외래 키를 참조로 변환하는 일도 자동으로 해준다!
객체 그래프 탐색
NOTE
•
객체에서 회원이 소속된 팀을 조회할 때는 참조를 사용해서 연관된 팀을 찾으면 된다. 이것을 객체 그래프 탐색이라 함
객체 연관관계 그림
◦
객체 탐색은 member.getOrder().getOrderItem()… 와 같이 자유로운 객체 탐색이 가능해야한다
◦
그런데 SQL을 직접 다루면 객체 그래프를 탐색할 수 있을까?
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T
ON M.TEAM_ID = T.TEAM_ID
SQL
복사
▪
위와 같이 MemberDAO에서 member객체를 조회할 때 다음과 같은 SQL을 실행했다고 생각하자
▪
회원(Member)와 팀(Team)에 대한 데이터는 조회가능하지만, 다른건 불가능함
◦
SQL을 직접 다루면 처음 실행하는 SQL에 따라 객체 그래프 탐색 범위가 정해진다!
▪
이는 테이블 조회 경우의 수마다 메소드를 만들게 되는 문제가 발생하게됨..
•
JPA를 통한 객체 그래프 탐색문제 해결
◦
JPA를 사용하면 이제 자유롭게 탐색이 가능해진다
◦
JPA는 실제 객체를 사용하는 시점까지 데이터베이스 조회를 미룬다고 해서 지연로딩(Lazy)라고 한다
JPA 비교
NOTE
•
데이터베이스는 기본 키의 값으로 각 row를 구분한다
•
객체는 동일성(identity) 비교와 동등성(equality)비교라는 2가지 방법이 존재한다
•
발생문제
class MemberDAO {
public Member getMember(String memberId) {
String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID = ?";
... // JDBC API, SQL 실행
return new Member(...);
}
}
Java
복사
String memberId = "010";
Member member1 = memberDao.getMember(memberId);
Member member2 = memberDao.getMember(memberId);
member1 == member2; //다르다
Java
복사
◦
member1과 member2는 동일한 키로 조회한 결과이다.
▪
테이블에서는 같은 Row에 해당한다
▪
객체에서는 새로운 객체가 생성되었기에 다른 객체가 된다
•
JPA를 통한 비교 해결
◦
JPA는 같은 트랜잭션일 떄 같은 객체가 조회되는 것을 보장한다.
JPA의 성능 최적화 기능
NOTE
•
1차 캐시와 동일성 보장
•
트랜잭션을 지원하는 쓰기 지연
•
지연 로딩
1차 캐시와 동일성 보장
NOTE
•
같은 트랜잭션 안에서는 같은 entity를 반환 → 약간의 조회 성능 향상
•
DB isolation Level이 Read Commit이어도 애플리케이션에서 Repetable Read 보장
String memberId = "100";
Member m1 = jpa.find(Member.class, memberId);
Member m2 = jpa.find(Member.class, memberId);
println(m1 == m2); // true
Java
복사
위의 코드에서 SQL은 한번만 실행된다!
트랜잭션을 지원하는 쓰기 지연 - INSERT
NOTE
•
트랜잭션을 commit할 때까지 INSERT SQL을 모은다
•
JDBC BATCH SQL 기능을 사용해서 한번에 SQL 전송
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 모아서 보낸다.
transaction.commit(); // [트랜잭션] 커밋
Java
복사
지연 로딩과 즉시 로딩
NOTE
•
지연 로딩 : 객체가 실제 사용될 때 로딩
◦
member를 조회 했을 떄, member만 가져온다
◦
그리고 3번째 줄에서 team.getName을 했을 때, jpa가 db에 team에 대한 query를 날려서 가져온다.
•
즉시 로딩 : JOIN SQL로 한번에 연관된 객체까지 미리 조회
◦
member를 조회 했을 때, member와 연관된 객체까지 모두 가져온다
•
member를 가져왔을 때 member만 쓴다고 하면 지연로딩
•
member를 가져왔을 떄, 다른 연관 객체도 쓰는 경우 즉시로딩