JPA에서 가장 중요한 것
- 객체와 관계형 데이터베이스 매핑하기
- 영속성 컨텍스트
개념
엔티티 매니저 팩토리와 엔티티 매니저
영속성 컨텍스트
엔티티를 영구 저장하는 환경
EntityManager.persist(entity)
persist는 DB에 저장하는것이 아니라 영속성 컨텍스트에 저장한다는 의미이다
영속성 컨텍스트는 논리적인 개념
엔티티 매니저를 통해서 영속성 컨텍스트에 접근함
엔티티의 생명주기
비영속 (new/transient)
영속성 컨텍스트와 전혀 관계가 없는 새로운 상태영속 (managed)
영속성 컨텍스트에 관리되는 상태준영속 (detached)
영속성 컨텍스트에 저장되었다가 분리된 상태삭제 (removed)
삭제된 상태
비영속
- 현재 JPA랑 관계없음
영속
- 객체를 저장한 상태
- 영속성 컨텍스트에 저장하지만 DB쿼리는 없음
- Transaction commit 해야만 DB에 쿼리 날림
- persist(),find() 를 할때도 영속 컨텍스트에 없엇다면, 생김
준영속
- 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
삭제
- 객체를 삭제한 상태(삭제)
- DB에서 해당 객체의 데이터 삭제 요청
em.remove(member)
영속성 컨텍스트의 이점
- 1차 캐시
- 동일성(identity) 보장
- 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
- 변경 감지(Dirty Checking)
- 지연 로딩(Lazy Loading)
1차 캐시
- DB 트랜잭션 단위로 영속 컨텍스트가 존재함(트랜잭션 끝나면 삭제됨)
- 따라서 큰 효과는없음
동일성
- 한 트랜잭션에서 같은 객체 find시 객체 참조값 같음. Equal
쓰기 지연
- commit 이전까지 계속 영속 컨텍스트에만 쌓아둠
- SQL문은 쓰기 지연 SQL 저장소에 쌓아둠 (trancsaction.commit시 DB에 쿼리 날림)
- Hibernate의
batch_size
기능을 활용하여, SQL문을 한번에 보낼수있다.
1 | EntityManager em = emf.createEntityManager(); |
변경 감지 (더티 체킹)
- 한 트랜잭션 안에서 변경사항 발생시 자동으로 DB에 마지막 쿼리로 update함
- Transaction.commit() 시에 영속 컨텍스트의 스냅샷(읽은 최소 객체 상태 기억)과 비교하여 다른점을 update함
1 | EntityManager em = emf.createEntityManager(); |
제거(엔티티 삭제)
- commit 시점에 delete쿼리 나감
1 | //삭제 대상 엔티티 조회 |
플러시(flush)
영속성 컨텍스트의 변경내용을 데이터베이스에 반영
(1차 캐시는 지워지지않는다. 트랜잭션이 끝나지 않았기 때문이다.)
동작
- 변경 감지
- 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송 (등록, 수정, 삭제 쿼리)
- 앞에 1,2번 진행후 DB에 쿼리 박히는 3번 진행함
영속성 컨텍스트를 플러쉬하는 방법
- em.flush() - 직접 호출
- 트랜잭션 커밋 - 플러시 자동 호출
- JPQL 쿼리 실행 - 플러시 자동 호출
(em.createQuery(“[JPQL문]”)=>발생시 이때 DB에 이전 쿼리 반영하고난후 JPQL 동작 호출됨)
플러쉬 요약
- 영속성 컨텍스트를 비우지 않음
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
- 트랜잭션이라는 작업 단위가 중요 -> 커밋 직전에만 동기화 하면 됨
준영속 상태
- 영속 -> 준영속
- 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(deteched)
- 영속성 컨텍스트가 제공하는 기능을 사용 못함(더티 체킹 등등)
준영속 상태 만드는 법
- em.detach(entity)
특정 엔티티만 준영속 상태로 전환 - em.clear()
영속성 컨텍스트를 완전히 초기화 - em.close()
영속성 컨텍스트를 종료
JPA의 commit과 flush는 다르다.
- 트랜잭션을
begin()
으로 시작한 뒤commit()
하여 트랜잭션을 종료해야한다. commit()
을 수행하게 되면 내부적으로 EntityManager의flush()
메서드를 호출한 후 트랜잭션을 닫습니다- flush는 쿼리를 전송하는 역할이고, commit은 내부적으로 flush를 수행한뒤 트랜잭션을 끝내는 역할입니다
- 따라서, flush 로 전송된 쿼리는 rollback할 수 있다.(트랙잭션 중이므로), 하지만 commit은 트랜잭션을 끝내므로 rollback 할수없다.