[Spring] 영속성 컨텍스트(Persistence Context)

신범철·2023년 9월 21일
0

스프링부트

목록 보기
12/20

영속성 컨텍스트(Persistence Context)란?

  • 엔티티를 영구적으로 저장하는 환경
  • 논리적인 개념
  • Entity Manager를 통해 접근한다.
  • Entity Manager 인스턴스 생성 시 1 : 1로 영속성 컨텍스트가 생긴다.

영속성 컨텍스트 생명주기

비영속(new)

영속성 컨텍스트와 전혀 관계가 없는 새로운 상태

Member member = new Member();
member.setId("memberA");
member.setName("범철");

영속(managed)

  • 영속성 컨텍스트에 관리되는 상태
  • 1차 캐시에 저장된다.
Member member = new Member();
member.setId("memberA");
member.setName("범철");

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

/* 영속성 컨텍스트에 영속화 하는 것 */
em.persist(member);

준영속(detached)

  • 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 한 번 영속상태가 되었기 때문에 식별자가 존재한다.
  • 지연 로딩 불가능
  • persist()로 영속성 컨텍스트를 저장하는 것이 아니라 merge()로 재저장 해야 한다.
  • 이때 해당 객체가 영속성을 갖는게 아니라 영속성을 갖는 새로운 객체를 반환
/* 특정 객체를 영속성 컨텍스트에서 분리하는 것 */
em.detach(member);
/* 영속성 컨텍스트 전체를 비우는 것 */
em.clear();
/* 영속성 컨텍스트를 종료하는 것 */
em.close();

삭제(removed)

삭제된 상태

em.remove(member);

영속성 컨텍스트의 장점

  1. 1차 캐시
  2. 동일성 보장
  3. 트랜잭션을 지원하는 쓰기 지연
  4. 변경 감지
  5. 지연 로딩

1차 캐시

  • 영속성 컨텍스트는 자체적으로 캐시를 가지고 있다.

  • 영속성 컨텍스트에 영속화(managed) 되어있는 객체에 대한 조회시 캐시에서 가져온다.(시간적인 이점 < 구조적로 인한 다른 이점)

  • 영속화 되는 두가지 경우

    • em.persist()로 영속화

    • 캐시에 등록되어 있지 않을 때, em.find()하는 경우

동일성 보장

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

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

  • em.persist()는 영속화 하는 과정이고, DB에 저장하는 것은 아니다.

  • 영속화되어 관리되는 객체들은 반드시 flush과정을 거처야 실제 DB에 반영된다.
    (정확히는 flush 이후 commit되어야 저장된다.)

  • 영속화를 하면 2가지이 수행된다.

    • 해당 내용의 쿼리가 쓰기 지연 SQL 저장소에 저장된다.
    • 해당 객체가 1차 캐시에 저장된다.
  • 즉, 쓰기가 지연되었다가 flush로 인해 한번에 반영된다.

  • 동작

  • commit이 되면 flush 과정으로 인해 쌓인 쿼리가 수행된다.

  • flush이후 실제 DB에 저장하는 commit이 수행된다.

변경 감지(dirty checking)

  • 수정에 경우에 변경 감지를 주로 사용한다.

  • em.find()를 통해서 가져온 객체를 수정한 뒤 em.persist()를 해야 하는가?

    • No! 변경 감지를 통해서 알아서 수정쿼리가 등록된다.
  • 변경 감지가 어떻게 일어나는가?

    • 1차 캐시에는 pk, entity 말고도 스냅샷이라는 것을 저장한다.
    • 스냅샷은 해당 객체의 최초 조회상태를 기록한 상태이다.
    • 객체에 대한 수정이 일어나면 해당 객체의 스냅샷과 비교하고, 자동으로 update 쿼리문을 쓰기 지연 저장소에 저장한다.

플러시(Flush)란?

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

Flush가 발생하는 경우

  1. 개발자가 직접 호출
    em.flush()를 통해 직업 flush를 수행할 수 있다.
    (쿼리가 수행된다.)

  2. 트랜잭션 커밋 - 자동호출
    트랜잭션이 commit되면 자동으로 flush를 수행한다.

  3. JPQL 쿼리 수행 - 자동호출
    JPQL쿼리가 수행되면 디폴트로 flush가 수행된다.
    옵션으로 지정이 가능하지만 거의 사용하지 않는다.

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의 주의 사항

  1. 플러시는 영속성 컨텍스트를 비우는 것이 아니다.(1차 캐시 역시 살아있다.)
  2. 영속성 컨텍스트의 변경 내용을 DB에 동기화한다가 더 옳은 표현이다.

Commit이란?

  • 트랜잭션을 begin()으로 시작해서 트랜잭션의 종료를 알리는 기능
  • 커밋이 수행되면 내부적으로 EntityManager의 flush메서드를 호출한 뒤 트랜잭션을 닫는다.

Commit vs Flush

Flush는 쿼리를 전송하는 역할이고 commit은 내부죽으로 flush를 수행한 뒤 트랜잭션을 끝내는 역할이다.

즉, flush로 전송된 쿼리는 롤백할 수 있지만 commit은 트랜잭션을 끝내므로 rollback할 수 없다.

profile
https://github.com/beombu

0개의 댓글