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) 보장#

Member findMember = entityManager.find(Member.class, 101L);
Member findMember2 = entityManager.find(Member.class, 101L);
// 동일성 보장
System.out.println(findMember == findMember2); // true
  • 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() 시 벌어지는 일#

  1. flush()
  2. 현재 엔티티와 스냅샷 비교
  • 스냅샷? 처음 조회 시 상태
  1. 변경점 있을 시 UPDATE SQL 생성
  2. 쓰기 지연 SQL 저장소에 쿼리 전송
  3. 쓰기 지연 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에선 기본 생성자가 하나 있어야 함 (왜?)
Last updated on