03. 영속성 관리
영속성 컨텍스트#
JPA 가장 중요한 두가지#
- 객체와 관계형 데이터베이스 매핑하기
 - 영속성 컨텍스트 (실제 내부 동작방식과 관련)
 
엔티티 매니저 팩토리와 엔티티 매니저#
- EntityManagerFactory 를 통해 각 요청마다 EntityManager를 생성
 - DB 커넥션 풀을 하나 가져와 EntityManager가 사용하게 됨
 
영속성 컨텍스트#
엔티티를 영구 저장하는 환경EntityManager.persist(entity)- 영속성 컨텍스트를 통해 해당 엔티티를 영속화 한다는 뜻
 - 엔티티를 영속성 컨텍스트에 저장한다는 의미
 
- 영속성 컨텍스트는 논리적인 개념
 - EntityManager를 통해 영속성 컨텍스트에 접근하게 됨
 - EntityManager: PersistenceContext = 1: 1 (스프링에선 n:1)
 
엔티티의 생명 주기#
생명주기 추후 참고
비영속 (new/transient)#
- 객체만 생성된 상태
 
영속 (managed)#
em.persist(entity);를 실행하게 되면 영속 컨텍스트에 객체가 보관됨- 아직 쿼리가 안날아가고 앱단에 객체가 영속 컨텍스트에 있는 상태
 
- 아직 DB에 저장은 안되고 transaction에 commit 을 하면 그때 쿼리가 날아감
 
준영속 (detached)#
em.detach(entity);- 영속성 컨텍스트에서 등록되어 있던 객체를 떼어낸다는 의미
 
삭제 (removed)#
- 삭제된 상태
 em.remove(entity);
영속성 컨텍스트의 이점#
앱과 DB 사이 계층이 하나 존재하게 되면서 얻어지는 이점
엔터티 조회, 1차 캐시#
- EntityManager를 통해 조회시 1차 캐시부터 찾아봄
 - 1차 캐시에 없으면, DB에 조회한 결과를 1차 캐시에 저장 후 결과를 가져옴
 - 큰 도움은 안된다?
- EntityManager는 트랜잭션당 하나로 관리해서 실질적 도움은 안됨
 - 앱 전체 캐시는 2차 캐시라 불림
 
 
영속 엔티티의 동일성(Identity) 보장#
- Repetable Read 등급의 트랜잭션 격리 수준을 DB가 아닌 앱 차원에서 제공
 
트랜잭션을 지원하는 쓰기 지연 (엔티티 등록)#
- 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
transaction.commit()이때 INSERT SQL을 보내게 됨- EntityManager에 쓰기 지연용 SQL 저장소가 있음
- 쓰기 지연용 SQL에 persist 을 할 때
 - commit 시 SQL 저장소에 있던 쿼리를 DB에 전송
 - 그리고 SQL 저장소 쿼리들을 flush 함
 
 - 쓰기 지연을 하지 않고 바로바로 쿼리를 보내게 되면 성능 튜닝을 힘듦
 - persistence.xml 의 
hibernate.jdbc.batch_size옵션- 쿼리를 얼마나 한꺼번에 한 커넥션에 보낼지 지정하는 기능
 
 
 
변경 감지 (엔티티 수정)#
- 영속 엔티티 데이터 수정 (Dirty Checking)
persist를 따로 코딩 하지 않고 변경 쿼리를 만들어 커밋시 보내게 됨
 
tx.commit() 시 벌어지는 일#
- flush()
 - 현재 엔티티와 스냅샷 비교
 
- 스냅샷? 처음 조회 시 상태
 
- 변경점 있을 시 UPDATE SQL 생성
 - 쓰기 지연 SQL 저장소에 쿼리 전송
 - 쓰기 지연 SQL 저장소 flush
 
플러시#
- 영속성 컨텍스트의 변경내용을 DB에 반영
- write-behind SQL 저장소에 쿼리를 날림
 
 
flush 발생#
- Dirty Checking
 - 수정된 엔티티를 쓰기 지연 SQL 저장소에 저장
 - 쓰기 지연 SQL 을 DB에 전송
 - 쓰기 지연 SQL 저장소를 flush
 
영속성 컨텍스트에 플러시 발생 시점#
- 직접 명령: 
em.flush() - 트랜잭션 커밋 
tx.commit() - JPQL 쿼리 실행 시
 
플러시 모드 옵션#
em.setFlushMode(FlushModeType.COMMIT)- FlushModeType.AUTO
- 커밋이나 쿼리를 실행할 때 플러시 (기본값)
 
 - FlushModeType.COMMIT
- 커밋할때만 플러시
 - JPQL 쿼리 실행 시 쿼리가 나가버리는 현상을 방지 가능
 - 뭐 도움 안될 수도 있어서 AUTO를 권장
 
 
유의 사항#
- 영속성 컨텍스트를 비우는게 아님!
 - 영속성 컨텍스트의 변경내용을 DB에 동기화
 - 트랜잭션 작업 단위가 중요!
- 커밋 직전에만 동기화 하면 됨
 
 
준영속 상태#
- 영속 -> 준영속
 - 1차 캐시에 올라간 상태가 영속 상태
- 조회를 해도 1차 캐시에 올라감
 
 - 영속성 컨텍스트의 기능을 사용 못함
 
준영속 상태 만드는 법#
em.detach(entity)- 특전 엔티티 객체를 대상으로 준영속 상태로 만듦
 
em.clear()- 완전 초기화
 
em.close()- 영속성 컨텍스트 완전 종료 시켜서 못쓰게 만들기
 
정리#
- 영속성 컨텍스트에 대해 알아봄
- JPA 실제 돌아가는 방식
 - 엔티티를 저장하는 환경
 
 - EntityManager를 통해서 접근
- EntityManager: PersistenceContext = 1:1
 - Spring에선 EM:PC = n:1 가 됨
 
 - 상태
- 비영속, 영속, 준영속, 삭제
 
 - 영속성 컨텍스트 이점
- 1차 캐시
 - Identity 보장
 - transactional write-behind (트랜잭션을 지원하는 쓰기 지연)
 - Dirty Checking (변경 감지)
 - Lazy Loading (지연 로딩) -> 추후 설명
- member.getTeam() 시 쿼리가 날라감
 
 
 - flush
- AUTO 권장
 - 영속성 컨텍스트를 비우지 않음
 - DB에 영속성 컨텍스트를 동기화
 - 트랜잭션 작업 단위
 
 - 준영속 상태
 
추가 공부#
- Repetable Read 등급의 트랜잭션 격리 수준을 DB가 아닌 앱 차원에서 제공
- 음 어떤 말인지 조금더 공부해야 할듯
 
 - JPA Entity에선 기본 생성자가 하나 있어야 함 (왜?)