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

Noah-wilson·2024년 12월 26일

JPA

목록 보기
4/10

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

엔티티를 영구히 저장하는 환경이란 뜻으로, 논리적인 개념이다.
즉, 영속적인 엔티티의 고유ID(고유 식별자)마다 고유한 엔티티 인스턴스를가진 인스턴스의 집합이다.(키:값 구조)

영속성 컨텍스트의 장점

  1. 1차 캐시

참고. 애플리케이션 단위의 영속성 컨텍스트= 2차캐시
  1. 영속성 엔티티의 동일성 보장

영속성 컨텍스트는 데이터베이스의 REPEATABLE READ 기능처럼 트랜잭션 내에서 동일한 데이터를 읽을 때 항상 동일한 결과를 보장하며, 애플리케이션 차원 에서 엔티티의 동일성을 유지해준다.

Member member1 = em.find(Member.class, "member1"); 
Member member2 = em.find(Member.class, "member1");
System.out.println(member1 == member2); //동일성 비교 true
  1. 트랜잭션을 지원하는 쓰기 지연

예제:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();

Member member1 = new Member(150L,"A");
Member member2 = new Member(160L,"B");
            
transaction.begin(); // [트랜잭션] 시작

//바로 INSERT SQL을 데이터베이스에 보내지 않는다.
em.persist(member1);
em.persist(member2);

System.out.println("==============");

//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋

실행 결과:

INFO: HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
==============
Hibernate: 
    /* insert for
        hellojpa.Member */insert 
    into
        Member (name, id) 
    values
        (?, ?)

실행 결과를 보면 'em.persist(member1);'를 한 시점에 INSERT SQL을 하지 않는 모습을 볼 수 있다.
그럼 만약 commit 되지 않았을때 member1과 member2를 조회한다고 하면 조회가 안되지 않을까?
정답은 조회가 된다
persist() 호출 후 데이터베이스에 반영되지 않은 상태일때 INSERT SQL을 하지 않지만, 영속성 컨텍스트에 해당 엔티티를 관리하고 있기 때문이다.
예제:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();

Member member1 = new Member(150L,"A");
Member member2 = new Member(160L,"B");
            
transaction.begin(); // [트랜잭션] 시작

//바로 INSERT SQL을 데이터베이스에 보내지 않는다.
em.persist(member1);
em.persist(member2);

 System.out.println("==============");
Member member = em.find(Member.class, 150L);
System.out.println("member = " + member.getName());
           
System.out.println("==============");

//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋

실행 결과:

==============
member = A
==============
Hibernate: 
    /* insert for
        hellojpa.Member */insert 
    into
        Member (name, id) 
    values
        (?, ?)

동작 원리는 아래 그림을 참고하자.

1차 캐시에 member1이 등록되고 동시에 JPA가 Entity를 분석하여 쓰기지연 SQL 저장소에 INSERT SQL을 생성하여 쌓아놓는다.


트랜잭션이 COMMIT되거나 엔티티 매니저가 FLUSH되면 쓰기지연 SQL저장소에 저장된 INSERT, UPDATE, DELETE와 같은 SQL문이 데이터베이스로 전달되고 실제로 실행된다.
즉 버퍼링 기능을 갖고 있다고 볼 수 있다.

플러시에 대한 자세한 내용은 플러시 란? 를 참고하자.

4.변경감지(Dirty Checking)

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속성 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//em.update(member) 이런 코드가 있어야 하지 않을까?
transaction.commit(); // [트랜잭션] 커밋

+스냅샷이란? 값을 읽어온 시점의 엔티티 상태

JPA는 트랜잭션을 커밋하는 시점에 내부적으로 FLUSH가 호출된다. 1차 캐시에서 Entity와 스냅샷을 비교 후 상태가 변경된 Entity에 대한 UPDATE SQL을 생성 후 쓰기지연 SQL 저장소에 저장한다.
그 후 데이터 베이스에 UPDATE SQL을 반영한다.
이 과정을 변경감지(Dirty Checking)라고 한다.

0개의 댓글