JPA (3) JPA 영속성 관리
27 Feb 2022김영한님의 자바 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);
- 삭제된 상태
- 비영속(new/transient)
- 영속성 컨텍스트의 장점
- 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
격리수준을 애플리케이션 차원에서 보장 가능
- 1차 캐시로
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)
- 1차 캐시
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()
: 영속성 컨텍스트 종료