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에선 기본 생성자가 하나 있어야 함 (왜?)