JPA 영속성 컨텍스트

이상민·2021년 9월 29일
0

JPA

목록 보기
1/8
post-thumbnail

1. EntityManagerFactory와 EntityManger

Entity : RDB의 Table과 맵핑되는 객체

@Entity
@Table(name = "customers")    // 지정하지 않을시 클래스명 사용
@Getter
@Setter
public class Customer {
    @Id private long id;      // pk 설정 
    private String firstName;
    private String lastName;
}
  • EntityManagerFactory : Entity를 관리하는 EntityManager를 생산하는 팩토리. Thread-safe하다

  • EntityManager : Entity를 저장하고, 수정하고, 삭제하고, 조회하는등 Entity와 관련된 모든 일을 처리한다. Thread-safe하지 않다

하나의 팩토리를 만들고 요청별로 매니저를 생성하는 방식으로 작동된다. 엔티티 매니저는 트랙잭션을 시작(BEGIN)할때 커넥션 풀에서 커넥션을 획득한다


2. 영속성 컨택스트

JPA를 이용하는데 가장 중요한 요소
엔티티를 영구 저장하는 환경. 엔티티매니저는 엔티티를 영속성 컨텍스트에 보관하고 관리한다

특징

  • 논리적인 개념이다

  • 키값으로 엔티티를 1차 캐시에서 관리하기 때문에 엔티티는 id값을 반드시 가져야한다

  • JPA는 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 DB에 flush한다

  • flush는 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 작업이다

  • 영속성 컨텍스트가 엔티티를 관리하므로 얻는 이점은 다음과 같다

    • 1차 캐시
    • 동일성 보장 : 1차 캐시로 REPEATABLE READ 격리 수준을 애플리케이션 차원에서 제공
    • 트랜잭션을 지원하는 쓰기 지연
    • 변경 감지
    • 지연 로딩
  • 보통 트랜잭션 단위로 사용하기 때문에, 하나의 데이터베이스 트랜잭션 종료시 영속성 컨택스트도 종료된다


3. 엔티티의 생명주기

3-1. 비영속(new/transient)

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

Customer customer = new Customer();
customer.setId(1L);
customer.setFirstName("Sangmin");
customer.setLastName("Lee");

3-2. 영속 (mananged)

영속성 컨텍스트에 관리된 상태

// 영속성 컨텍스트 생성 
EntityManager em = new EntityManager();
em.persist(customer);

3-3. 준영속 (detached)

영속성 컨텍스트에 저장되었다가 분리된 상태

em.detach(customer);  // 영속 상태 엔티티를 분리
em.clear();           // 영속 상태 모든 엔티티를 분리
em.close();           // 영속성 컨텍스트 종료 

3-4. 삭제 (removed)

삭제된 상태

// customer 엔티티를 영속성 컨텍스트에서 분리하고 DB에서도 삭제 
em.remove(customer);

4. 영속성 컨텐스트를 통한 CRUD

// Given
customerA = new Customer(1L);  // id = 1
customerB = new Customer(2L);  // id = 2

4-1. 저장

// EntityManagerFactory에서 엔티티 매니저 생성
EntityManager em = emf.createEntityManager();  

// 트랜잭션 획득
EntityTransaction transaction = em.getTransaction();  
transaction.begin();

// 영속성 컨텍스트에 등록하고 1차 캐시와 쓰기 지연 저장소에 저장
em.persist(customerA);

// 트랜잭션이 커밋되면 DB에 트랜잭션 실행 
transaction.commit(); 

4-2. 조회

캐시를 통한 조회

EntityManager em = emf.createEntityManager();
EntityTransaction transaction - em.gtTransaction();
transaction.begin();
em.persist(customerA);  // 1차 캐시에 customerA 존재 
transaction.commit();

// 1차 캐시에서 customerA 조회 
// DB로 쿼리가 발생하지 않음 
Customer customer = em.find(Customer.class, 1L);

DB를 통한 조회

EntityManager em = emf.createEntityManager();
EntityTransaction transaction - em.gtTransaction();
transaction.begin();
em.persist(customerB);  
transaction.commit();

// 영속성 컨텍스트 초기화
em.clear();   // 1차 캐시에 customerB 존재 X

// DB에서 customerB 조회 
// customerB를 조회하며 1차 캐시에도 저장함 
Customer customer = em.find(Customer.class, 2L);

// 1차 캐시에서 조회 
Customer entity = em.find(Customer.class, 2L);

4-3. 수정

// customerA 영속화 되어있을 때
transaction.begin();

Customer customer = em.find(Customer.class, 1L);
customer.setFirstName("Sangmin");
customer.setLastName("Lee");

// flush() 호출 시 변경사항을 감지하고 UPDATE 쿼리를 수행한다 
transaction.commit();
  • JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해 저장하는데 이를 스냅샷이라한다. 이후 flush() 시점에 스냅샷과 엔티티를 비교해 변경된 엔티티를 찾고, 존재할 경우 update 쿼리를 수행한다

4-4. 삭제

// customerA가 영속화 되어있을 때
transaction.begin();

Customer customer = em.find(Customer.class, 1L);
em.remove(customer);

transaction.commit();
profile
편하게 읽기 좋은 단위의 포스트를 추구하는 개발자입니다

0개의 댓글