[데브코스] JPA - 영속성 컨텍스트

Updated: Categories:

W8D2 Part1 - Entity Manger / 영속성 컨텍스트에 대해 알아보자

Entity Manager

Entity Manager Factory

  • Entity Manager를 생성해주는 Bean
  • Thread Safe 하다.

Entity Manager

  • Entity 객체를 관리(영속성 컨텍스트에 보관)하고 CRUD까지 모두 처리해준다.
  • Thread Safe 하지 않음. 즉 여러 스레드가 존재한다면 동시성 이슈가 발생할 수 있다.

image

  • DB에 커밋이 일어나는 시점에만 Entity Manger에서 커넥션을 가져와 사용한다.
  • 트랜잭션의 경우 트랜잭션이 시작되는 시점에서 커넥션을 획득함


Persistence Context

  • 영속성 컨텍스트는 JPA에서 가장 중요한 요소임
  • Entity를 영구 저장하는 환경이라는 뜻

image

주요 특징

  • 영속성 컨텍스트 내의 Entity는 식별자 값이 반드시 필요하다
    • 1차 캐시에서 Key-Value 형태로 Entity를 관리하기 때문
  • 트랜잭션을 커밋하는 시점에서 영속성 컨텍스트에 새로 저장된 Entity를 DB에 반영(FLUSH)한다
  • Flush는 영속성 컨텍스트의 변경내용을 DB에 동기화하는 작업

장점

  • 1차 캐시
  • 동일성 보장
  • 트랙잭션을 지원하는 쓰기 지연
  • 변경 감지
  • 지연 로딩


Entity Life Cycle

image

상태

  • 비영속 (new / transient) : 영속성 컨텍스트와 전혀 관계가 없는 상태
  • 영속 (managed) : 영속성 컨텍스트에 저장된 상태
  • 준영속 (detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 삭제 (removed) : 삭제된 상태

동작

  • 어떤 Entity 객체가 있다고 했을 때 영속성 컨텍스트의 동작은 아래와 같다
Entity entity = new Entity();
... 객체에 데이터 삽입 ...

// 영속성 컨텍스트에 엔티티 등록
entityManager.persist(entity);

// 영속성 컨텍스트에서 해당 엔티티 분리
entityManager.detache(entity);
// 영속성 컨텍스트 내부 모든 엔티티 분리
entityManager.clear();
// 영속성 컨텍스트 종료
entityManager.close();

// 영속성 컨텍스트에서 해당 엔티티 분리 및 DB에서 삭제
entityManager.remove(entity);


영속성 컨텍스트를 통한 CRUD (예시)

Create

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

EntityTransaction transaction = em.getTransaction(); // 2)트랜잭션 획득
transaction.begin(); // 3)트랙잰셕 begin

Customer customer = new Customer(); // 4-1)비영속
customer.setId(1L);
customer.setFirstName("honggu");
customer.setLastName("kang");

em.persist(customer); // 4-2)영속화

transaction.commit(); // 5)트랜잭션 commit
// 트랜잭션이 커밋이 되는 순간 쿼리가 수행된다. flush DB와 동기화가 된다.

image

  • 영속화된 Entity와 관련된 쿼리가 커밋전에는 쓰기 지연 저장소에 잠시 저장된다.


Read (1차 캐시)

... Create 코드와 동일 ...

Customer entity = em.find(Customer.class, 1L); // 1차 캐시에서 조회한다.
log.info("{} {}", entity.getFirstName(), entity.getLastName());

image

  • find시 DB에 Entity에 해당하는 쿼리를 날리지 않고 1차 캐시에서 정보를 가져온다.


Read (DB)

... Create 코드와 동일 ...

em.clear(); //영속성 컨텍스트를 초기화 한다.

Customer entity = em.find(Customer.class, 1L); // DB 에서 조회한다. SELECT ...
log.info("{} {}", entity.getFirstName(), entity.getLastName());
em.find(Customer.class, 1L); // SELECT Query 수행되지 않는다. 1차캐시 사용

image

  • 영속성 컨텍스트를 초기화했기 때문에 1차 캐시에는 아무런 내용이 없음
  • 따라서 첫번째 find시 DB에 직접 쿼리를 날려 조회한다.
  • 두번째 find시엔 앞에서 쿼리를 실행했었기 때문에 1차 캐시에 해당 Entity 정보가 담겨있음.


Update

... Create 코드와 동일 ...

transaction.begin();

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

// entity 객체 수정
entity.setFirstName("guppy");
entity.setLastName("hong");

transaction.commit(); // flush -> UPDATE ... 

image

  1. 커밋(Flush) 발생
  2. Entity를 기존 스냅샷(최초의 Entity 상태)과 비교
  3. 스냅샷 변경감지(dirty checking)시 변경 쿼리를 쓰기 지연 저장소에 저장
  4. 커밋시 저장된 쿼리 실행


Delete

... Create 코드와 동일 ...

transaction.begin();

Customer entity = em.find(Customer.class, 1L);
em.remove(entity);
    
transaction.commit(); // flush -> DELETE ..
  1. 영속성 컨텍스트에서 entity 삭제
  2. 커밋(Flush) 발생시 삭제 감지 후 쿼리 실행