JPA (3) JPA 영속성 관리

김영한님의 자바 ORM 표준 JPA 프로그래밍 강의 정리

영속성 컨텍스트

  • JPA에서 가장 중요한 것
    • 객체와 RDB 매핑하기 → ORM
    • 영속성 컨텍스트
  • 영속성 컨텍스트
    • JPA를 이해할 때 가장 중요한 용어
    • 엔티티를 영구 저장하는 환경
    • EntityManager.persist(entity);
    • 영속성 컨텍스트는 논리적 개념으로 엔티티 매니저를 통해 접근 가능
  • 엔티티 생명주기
    • 비영속(new/transient)
      • 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
      • 엔티티가 영속성 컨텍스트에 들어있지 않은 상태
      • 아래 코드에서는 객체를 생성했지만 엔터티 매니저를 통해 저장되지는 않은 상태
          Member member = new Member();
          member.setId(1L);
          member.setName("A");   
        
    • 영속(managed)
      • 영속성 컨테스트에 관리되는 상태
      • 엔티티가 영속성 컨텍스트에 속해 있음
      • persist 할 때는 DB에 저장되지 않고 트랜잭션에서 커밋해야 DB에 저장됨
      Member member = new Member();
      member.setId(1L);
      member.setName("A");   
        
      EntityManager em = emf.createEntityManager();
      em.getTransaction().begin();
        
      em.persist(member); // 객체를 저장한 상태 & 영속 상태
    
    • 준영속(detached)
      • 영속성 컨텍스트에 저장되었다가 분리된 상태
      em.detach(member);
    
    • 삭제(removed)
      • 삭제된 상태
          em.remove(member);
        

        entity cycle

  • 영속성 컨텍스트의 장점
    • 1차 캐시
      • 엔티티를 조회할 때, 1차 캐시에서 먼저 찾게 되며 엔티티가 영속 상태일 경우 1차 캐시에서 조회 가능
      • 1차 캐시에 없을 때는 DB에서 조회하고 1차 캐시에 저장 후 반환
      • 하나의 트랜잭션 안에서만 유효함
      Member member = new Member();
      member.setId(1L);
      member.setName("A");   
        
      em.peresist(member); // 1차 캐시에 저장됨
        
      // 1차 캐시에서 조회
      Member findMember = em.find(Member.class, 1L);
    
    • 동일성(identity) 보장
      • 1차 캐시로 REPEATABLE READ 격리수준을 애플리케이션 차원에서 보장 가능
      Member findMember = em.find(Member.class, 2L);			
      Member findMember2 = em.find(Member.class, 2L);
        
      System.out.println(findMember == findMember2); // true
    
    • 트랜잭션을 지원하는 쓰기 지연
      • 영속성 컨텍스트 내부에 쓰기 지연 SQL 저장소가 있어 커밋하기 전까지 persist()로 1차 캐시에 저장된 INSERT 쿼리가 저장됨
      • 트랜잭션에서 커밋을 할 때 쓰기 지연 SQL 저장소의 INSERT 쿼리가 flush 되면서 DB에 커밋됨
      Member a = new Member(3L, "C");
      Member b = new Member(4L, "D");
        			
      em.persist(a);	// 1차 캐시에 저장됨
      em.persist(b);	// 쓰기 지연 SQL 저장소에 쿼리 저장
        
      tx.commit();	// DB에 저장됨
    
    • 변경 감지(dirty checking)
      • 트랜잭션에서 커밋하면 영속성 컨텍스트 내부에서 엔티티와 스냅샷 비교
      • 1차 캐시에서 스냅샷을 갖고 있고 새로 변경한 값과 다른 것을 확인
      • UPDATE 쿼리 생성 후 쓰기 지연 SQL 저장소에 저장
      • DB 반영
      Member a = em.find(Member.class, 3L);
      a.setName("X");	// em.persist()를 해주지 않아도 변경 사항이 적용됨
      tx.commit();
    
    • 지연 로딩(lazy loading)

flush

  • 영속성 컨텍스트의 변경 내용을 DB에 반영
  • 플러시가 발생하는 경우
    • 변경 감지
    • 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록할 때
    • 쓰기 지연 SQL 저장소의 쿼리를 DB에 전송할 때 (등록, 수정, 삭제)
  • 영속성 컨텍스트를 플러시하는 방법
    • 직접 호출: em.flush()
    • 자동 호출
      • transaction.commit()
      • JPQL 쿼리 실행
  • flush mode
    • em.setFlushMode()에서 설정
    • FlushModeType.AUTO: 커밋이나 쿼리를 실행할 때 플러시 (기본값)
    • FlushModeType.COMMIT: 커밋할 때만 플러시
  • 플러시를 실행한다고 영속성 컨텍스트를 비우지는 않음
  • 영속성 컨텍스트의 변경 내용을 DB에 동기화
  • 트랜잭션이라는 작업 단위가 중요하며 커밋 직전에만 동기화하면 됨

준영속 상태

  • 영속 상태는 1차 캐시에 저장됨
  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 상태
  • 영속성 컨텍스트가 제공하는 기능을 사용 X
Member a = em.find(Member.class, 3L);
a.setName("X");
em.detach(a);	// 위에서 업데이트 한 내용이 적용되지 않음
tx.commit();
  • 준영속 상태로 만드는 방법
    • em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
    • em.clear() : 영속성 컨텍스트 완전히 초기화
    • em.close() : 영속성 컨텍스트 종료