본문 바로가기

Spring/JPA

JPA- 영속성 컨텍스트

영속성 컨텍스트

 마치 어플리케이션과 DB 사이에 하나의 계층이 있다고 생각하면 된다.

 영속 컨텍스트의 장점 

 1) 엔티티가 영속성 컨텍스트에 저장되어 있는 경우

  영속성 컨텍스트가 있을때의 장점은 조회를 빠르게 진행할 수 있다는 것이다. 예를들어 Member 엔티티가 존재한다고 하자. 이 엔티티는 DB에 저장이 되어있을 뿐만 아니라 영속성 컨텍스트에도 저장이 되어있다. 그렇기 때문에 find()로 조회를 하는 경우에 DB에서 뒤질 필요없이 영속성 컨텍스트의 1차 캐시에서 먼저 찾아 반환을 하면된다.

 위 처럼 영속 컨텍스트에 id가 member1인 member 엔티티는 DB에서 찾을 필요 없이 영속 컨텍스트에서 조회하여 반환한다.

 영속 상태의 엔티티를 조회

package hellojpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try{
            //비영속
            Member member = new Member();
            member.setId("member1");
            member.setName("Member1");
            //영속
            em.persist(member);
            
            //영속성 컨텍스트에서 조회한 Member 엔티티
            Member findMember = em.find(Member.class,"member1");


            tx.commit();
        } catch(Exception e) {
            tx.rollback(); //오류 발생 시 롤백
        } finally {
            //종료
            em.close();
        }
        emf.close();
    }
}

 2) 엔티티가 DB엔 있지만 persist되어 있지 않은 상태에 조회하는 경우

 그런데 만일 DB에는 저장이 되어있지만 영속 컨텍스트에는 저장되어있지 않은 id가 "member2"인 member 엔티티가 있다고 하자. 그 경우에는 DB에서 해당 엔티티를 찾아서 1차 캐시(영속 컨텍스트)에 저장 후 저장된 엔티티를 반환한다.

 영속성 컨텍스트의 동일성 보장

 영속성 컨텍스트에서 꺼내온 객체는 항상 동일성이 보장된다.

Member member1 = em.find(Member.class, 1L);
Member member2 = em.find(Member.class, 1L);

System.out.println(member1 == member2);

>>true

 

중간 정리

  1. 팩토리에서 엔티티 매니저를 생성하면 그에 대한 영속 컨텍스트가 생성된다. 엔티티 매니저는 클라이언트 별로 다르므로 영속 컨텍스트도 클라이언트 별로 개별적이다.(엔티티 매니저는 트랜잭션 동안 존재하기 때문에 영속 컨텍스트도 한 트랜잭션이 끝나면 사라진다 <- DB와 차이점
  2. 엔티티를 persist를 하면 영속 컨테스트의 1차캐시에 저장되고 DB에도 저장이 된다. find를 하는 경우 1차 캐시에 저장이 되어있는 경우 1차캐시에서 가져오고, 없는 경우 DB에서 찾은 후 1차 캐시에 저장 후 값을 반환한다. 따라서 다음번에 조회하는 경우 이번에는 DB 대신 1차캐시에 이미 저장되어있는 엔티티를 가져온다.  

 

쿼리 분석

package hellojpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try{
           // member 엔티티는 이미 DB에 저장되어 있는 상황
            Member member1 = em.find(Member.class, 101L);
            Member member2 = em.find(Member.class, 101L);

            tx.commit();
        } catch(Exception e) {
            tx.rollback(); //오류 발생 시 롤백
        } finally {
            //종료
            em.close();
        }
        emf.close();
    }
}



Hibernate: 
    select
        member0_.id as id1_0_0_,
        member0_.name as name2_0_0_ 
    from
        Member member0_ 
    where
        member0_.id=?

 

 101L이 pk값인 member 엔티티가 이미 DB에 저장이 되어있는 상태에서 find를 2회 수행한 상황이다.

그리고 결과로는 select문이 1회만 실행되었다.

1) 첫번째 find문이 실행된 순간 영속 컨텍스트에는 저장된 값이 없기 때문에 select문을 실행해 DB의 엔티티를 찾아 영속 컨텍스트에 저장후 반환하였다.

2) 두번째 find문이 실행된 순간 영속 이미 엔티티는 영속 컨텍스트에 저장되어 있었기 때문에 select문을 실행할 필요없이 그냥 영속컨텍스트에 저장되어 있는 값을 가져왔기에 select 쿼리가 날라가지 않았다.

따라서 select 쿼리는 1회만 실행된 것이다.

 

준영속 상태

 더 이상 영속성 컨텍스트에서 관리하지 않는 엔티티를 준영속 엔티티라고 한다. Dirty checking을 지원하지 않는다.

영속 상태 정리

 참고로 원래 영속 엔티티였다가 em.clear(), em.close(), em.detach(member);처럼 영속 컨텍스트에서 분리된 경우 뿐만 아니라, 기존에 DB에 저장되었던적 있는 엔티티의 정보를 바탕으로 새롭게 만든 엔티티도 준영속 엔티티에 속한다.

'Spring > JPA' 카테고리의 다른 글

JPA- 중요! 엔티티 매핑  (0) 2023.02.28
JPA- flush와 영속성 관리  (0) 2023.02.28
JPA- EntityManager.find()로 조회하기  (0) 2023.02.16
JPA- 관련 개념 정리  (0) 2023.02.16
JPA- 지연 로딩과 즉시 로딩  (0) 2023.02.16