관계형 DB를 사용하려면 SQL문을 사용하는데,
관계형 DB와 객체지향 언어는 패러다임의 차이가 있다.
RDB에서는 상속관계를 표현하면, FK를 통해 부모테이블과 연관관계를 맺는다.
하지만 이렇게 한다고 해서 객체의 상속관계를 구현할 수 있는 게 아니다.
예를 들어 위 그림에서 객체 Album
을 DB에 저장한다면, Item
에 대한 정보를 Item
테이블에, Album
에 대한 정보를 Album
테이블에 각각 INSERT
해줘야 한다.
조회 과정에서도 각 테이블에 join SQL을 작성해주고, 각 객체를 생성해주는 복잡한 과정을 거친다.
여기까지만 봐도 SQL에 의존적이게 되어, 객체지향의 장점이 퇴색되고 있는 게 느껴진다.
또 모든 연관된 객체를 미리 로딩할 수는 없기 때문에, 객체 그래프 탐색 과정에서 엔티티 신뢰 문제도 발생한다.
ex) 가져온 Member 객체에 Team에 대한 정보가 주입돼있는지 신뢰할 수 없다.
//DB애서 조회
String memberId = "100";
Member member1 = memberRepository.getMember(memberId);
Member member2 = memberRepository.getMember(memberId);
member == member2; // false
//객체에서 조회
String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);
member == member2; // true
이러한 객체와 RDB의 패러다임 차이를 극복하기 위해 JPA를 이용한다.
JPA에서 가장 중요한 핵심 개념중 하나이다.
영속성 컨텍스트는 논리적인 개념
객체를 1차 캐시에 저장해두고, transaction이 끝나는 시점에서 commit 할 때 한 번에 데이터를 처리한다.
em.persist
를 통해 객체를 영속시킨다.
이렇게 했을 때 이점은 다음과 같다.
• 1차 캐시
• 동일성(identity) 보장
영속화하면, 1차 캐시에 id값으로 구분하여 Entity를 저장한다.
그리고 조회시 1차캐시를 먼저 확인한 후 없으면 DB를 조회하는 구조이다.
따라서 같은 Transaction안에서는 동일성이 보장된다.
트랜잭션을 지원하는 쓰기 지연 (transactional write-behind) : commit 시점에 한 번에 1차캐시에 있는 데이터들을 처리한다.
변경 감지 (Dirty Checking)
DB에서 데이터를 조회하면 자동으로 해당 객체를 영속성 컨텍스트로 관리한다.
영속성 컨텍스트에서 관리되는 객체는 수정시 자동으로 변경이 감지돼어(Dirty Checking) 값이 변경된다. -> update 쿼리를 따로 날릴 필요가 없어진다.
지연 로딩 (Lazy Loading)
프록시를 이용한 지연로딩이 가능해진다. 뒤에서 설명
플러시 : 영속성 컨텍스트의 내용을 DB에 반영
• em.flush()
- 직접 호출
• 트랜잭션 커밋 - 플러시 자동 호출
• JPQL 쿼리 실행 - 플러시 자동 호출
-> 1차 캐시의 내용을 모두 반영한 상태에서 DB정보를 이용해야 한다.
준영속 : 영속성 컨텍스트에서 엔티티를 분리시킨다. -> 영속성 컨텍스트가 제공하는 기능을 사용할 수 없다.
• em.detach(entity)
특정 엔티티만 준영속 상태로 전환
• em.clear()
영속성 컨텍스트를 완전히 초기화
• em.close()
영속성 컨텍스트를 종료
@Entity
가 붙은 클래스는 JPA가 관리, 엔티티라고 한다.
- 기본 생성자가 필수
- 저장할 필드에 final을 사용하면 안된다.
name 속성으로 이름을 지정할 수 있지만, 기본값(클래스 이름)사용을 권장
@Table
: 엔티티와 매핑할 테이블 지정 (기본값 = 엔티티 이름)
JPA는 DDL을 애플리케이션 실행 시점에 자동으로 생성한다.
데이터베이스 방언을 활용해서 사용하는 DB에 맞게 DDL이 생성된다.
실제 운영서버에서는 적절히 다듬은 후 사용해야 한다.
운영 장비에는 create, create-drop, update 를 사용하면 안된다.
데이터가 전부 날아가버릴 위험이 있다.
@Column
의 속성을 통해 제약조건을 추가할 수 있다.
nullable, length, 유니크 제약조건 등
@Column
: 컬럼 매핑
@Temporal
: 날짜 타입 매핑
속성
- TEmporalType.DATE : 날짜, 데이터베이스 date 타입과 매핑
ex) 2013-10-11
- TemporalType.TIME : 시간, 데이터베이스 time타입과 매핑
ex) 11:12:13
- TemporalType.TIMESTAMP : 날짜와 시간, 데이터베이스 timestamp타입과 매핑
ex) 2013-10-11 11:12:13
LocalDate, LocalDateTime을 사용할 때는 생략 가능
@Enumerated
: enum 타입 매핑
속성 : EnumType.ORDINAL, EnumType.STRING
-> 주의 : ORDINAL 속성을 사용하지 않는다. 순서로 저장시 추후 순서가 바뀌면 문제가 된다.
@Lob
: BLOB, CLOB 매핑
@Transient
: 매핑하지 않음
메모리상에서 임시로 어떤 값을 보관하고 싶을 때 사용한다.
@Id
: 기본키 지정@GeneratedValue
: 자동 생성속성
기본 키 제약 조건 : NOT NULL, 유일성, 불변성
비즈니스 로직에서 사용하는 값은 불변성을 보장하기 힘들다.
-> Long + 대체키 + 키 생성 전략 을 권장한다.