[3] JPA 프로그래밍 (3) - 영속성 컨텍스트(Persistence Context) ?

10925 단어 JPAJPA

JPA의 핵심 2가지

1) 영속성 컨텍스트(Persistence Context)
2) 객체 & 관계형 DB 매핑

영속성 컨텍스트(Persistence Context)

[ 설명 ]

  • 엔티티를 영구 저장하는 환경
  • 눈에 보이지 않는 논리적인 개념
  • Entity Manager를 통해 접근
// 영속 상태로 등록
em.persist(member);
  • Entity Manager Factory ? Entity Manager ?

[ Entity 의 생명주기 ]

  • 비영속 (new / transient)
    : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
Member member = new Member();
member.setId("memberA");
member.setName("kjw");
  • 영속 (managed)
    : 영속성 컨텍스트에 관리되는 상태
Member member = new Member();
member.setId("memberA");
member.setName("kjw");

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

/* 영속성 컨텍스트에 영속화 하는 것 */
em.persist(member);
  • 준영속 (detached)
    : 영속성 컨텍스트에 저장되었다가 분리된 상태
/* 특정 객체를 영속성 컨텍스트에서 분리하는 것 */
em.detach(member);
/* 영속성 컨텍스트 전체를 비우는 것 */
em.clear();
/* 영속성 컨텍스트를 종료하는 것 */
em.close();
  • 삭제 (removed)
    : 삭제된 상태
em.remove(member);

영속성 컨텍스트의 이점

1) 1차 캐시
2) 동일성(identity) 보장
3) 트랜잭션을 지원하는 쓰기 지연 (transactional wirte-behind)
4) 변경 감지 (Dirty Checking)
5) 지연 로딩 (Lazy Loading)
- 추후 설명!


1. 1차 캐시

  • 영속성 컨텍스트는 자체적으로 캐시(cache)를 가지고 있다
  • 영속성 컨텍스트에 영속화 되어있는 객체에 대한 조회시 cache에서 가져온다
    --> 사실 시간적인 이점보다는 이러한 구조덕분에 오는 다른 이점들이 크다
  • 영속화 되는 2가지 경우 (cache에 등록되는 2가지 경우)
    • em.persist()로 영속화
    • cache에 등록되어 있지 않을 때, em.find() 하는 경우

2. 동일성(identity) 보장

  • 하나의 Transaciton마다 하나의 영속성 컨텍스트가 생성되는데,
    이 때 같은 객체를 참조하면 항상 동일한 참조 객체를 가져온다.

3. 트랜잭션을 지원하는 쓰기 지연

[ 설명 ]

  • em.persist(member)은 영속화 하는 것이지, DB에 저장되는 것은 아니다
  • 속화되어 관리되는 객체들은 반드시 flush과정을 거처야 실제 DB에 반영된다
    (정확히는 flush 이후 commit되어야 저장됨)
  • 영속화를 하면 2가지 과정이 수행된다
    • 해당 내용의 쿼리가 쓰기 지연 SQL 저장소에 저장
    • 해당 객체가 1차 캐시에 저장
  • 결과적으로 쓰기가 지연되었다가 flush로 인해 한번에 반영된다

[ 동작 원리 ]

  • 쓰기 지연 SQL 저장소에 해당 내용의 쿼리가 저장
  • 1차 캐시에 해당 객체가 저장(없을 경우)
  • tx.commit()되면 flush과정으로 인해 쌓인 쿼리가 수행
  • flush이후 실제 DB에 저장하는 commit이 수행된다

4. 변경 감지 (Dirty Checking)

[ 의문 ]

  • JPA 관련 메서드 어디에도 수정에 관한 메서드는 없다?
    --> 객체의 수정에 관해서는 Dirty Checking이 있기 때문!

[ 설명 ]

  • em.find()를 통해 가져온 객체를 수정한 뒤 em.persist()를 해야하나?
       --> 그렇지 않다! Dirty Checking을 통해 알아서 수정 쿼리가 등록
  • Dirty Checking은 어떻게 일어나는가 ?
    • 1차 캐시에는 pk, Entity말고도 스냅샷이라는 것을 저장한다
    • 스냅샷은 해당 객체의 최초 조회상태를 기록한 상태이다
    • 객체에 대한 수정이 일어나면 해당 객체의 스냅샷과 비교하고, 자동으로 UPDATE쿼리문이 쓰기 지연 SQL 저장소에 저장된다

플러시 (flush) 란?

[ 개념 ]

  • 영속성 컨텍스트의 변경내용을 DB에 반영하는 것
  • flush 과정이 시작되면 쓰기 지연 SQL 저장소에 있는 쿼리가 수행된다

[ flush 하는 방법 ]

  • 직접호출
    : em.flush()를 통해 직접 flush를 수행할 수 있음
      (flush가 호출되기 때문에 쿼리가 수행된다!)
  • Transaction Commit - 자동 호출
    : 트랜잭션이 commit되면 자동으로 flush를 호출한다
  • JPQL 쿼리 수행 - 자동 호출
    : JPQL쿼리가 수행되면 기본적(default)으로 flush가 수행된다
    • 옵션을 통해 지정 가능 (거의 안쓰임)
      • FlushModeType.AUTO : 커밋이나 쿼리를 실행시 플러시
      • FlushModeType.COMMIT : 커밋할 때만 플러시
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);

// 중간에 JPQL 실행 --> flush 자동 수행!
// 지금까지 영속화로 저장된 모든 쿼리문이 수행되게 된다
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();

[ 주의 ]

  • 플러시(flush)는 영속성 컨텍스트를 비우는 것이 아니다!
    (flush한다고 1차 캐시가 지워지는 것이 아님)
  • 영속성 컨텍스트의 변경 내용DB에 동기화 한다고 이해해야한다

좋은 웹페이지 즐겨찾기