JPA.02 Persistence Context & Entity Manager

Persistence Context 영속성 컨텍스트

JPA 를 이해하기 위해서는 중요한 개념이 바로 영속성 컨텍스트 인 것 같다. 개인적으로 영속성이란 말부터 생소하였고, 용어부터 생소하다보니 이해하기가 다소 까다로웠지만, 한번 이해를 해두면 JPA 를 이해하고 활용하는데 큰 도움이 되었다.

영속성 컨텍스트 역할

Entity 객체 보관 및 관리 역할 (영속성 관리)

영속성 컨텍스트는 어플리케이션과 DB 사이에서 Entity 객체 보관을 하고, 관리한다.

영속성 이란 ? JPA 가 관리하는 Entity 객체가 관리되고 있는 상태를 뜻한다.

Proxy 객체 활용한 기능 제공

영속성 컨텍스트는 프록시 객체를 활용하여 1차 캐시, 동일성 보장, 쓰기 지연, 변경 감지, 지연 로딩 같은 기능을 제공한다.

위와 같은 기능은 어플리케이션의 조금이나마(?) 성능을 향상시키는 장점을 가지고 있다.

Proxy 프록시 관련 내용은 JPA.05 연관관계 관리 에서 자세히 다룰 예정이다.


Entity Manager 엔티티 매니저

Entity Manager 엔티티 매니저는 영속성 컨텍스트와 연결하는 API를 제공하고 엔티티를 관리한다. 엔티티 매니저와 영속성 컨텍스트의 관계는 기본적으로 1 대 1 관계로 이루어진다.

하지만, Spring F/W 와 같은 멀티 스레드 환경에서는 N 대 1 관계로 구성되어있으며, Spring Data JPA 와 같은 프레임워크의 라이브러리를 통해 개발 구현 가능하다.

Entity Manager 구성

Entity 의 생명 주기

Entity 엔티티의 생명 주기는 아래와 같은 3가지 상태별 관리된다.

구분 내용
비영속성 - 영속성 컨텍스트에 저장되어 있지 않은 상태
영속성 - 영속성 컨텍스트에 저장되어 있는 상태
- 영속성 컨텍스트에서 저장된 엔티리를 관리하는 상태
준영속성 - 영속성 컨텍스트에서 엔티티가 분리된 상태
- 비영속의 객체와는 다르게 관리용 식별자를 가진 상태

Spring Data JPA 의 영속성 관리

  • Spring Data JPA 에서의 엔티티의 생명 주기는 트랜잭선의 의해 관리된다.
  • 트랜잭션이 커밋되는 순간, 해당 엔티티의 영속성 관리도 종료된다.

영속성 컨텍스트의 특징

앞서 영속성 컨텍스트는 Proxy 객체를 활용을 통해 다양한 기능을 제공한다고 했다. 그 기능들은 JPA 성능을 향상시켜주며, 패러다임의 불일치를 해결하는 역할을 한다.

1차 캐시

  • 영속성 컨텍스트에서 관리하는 엔티티에 대해서는 DB 조회 없이 엔티티를 반환한다.

영속성 컨텍스트의 특징 - 1차 캐시

동일성 보장

  • 영속성 상태의 엔티티에 대해서는 동일성을 보장한다.
Member member1 = em.find(Member.class, "memberA");    // DB 조회를 통해 생성된 Entity 객체
Member member2 = em.find(Member.class, "memberA");    // 1차 캐시를 통해 생성된 Entity 객체

System.out.println(member1 == memeber2);    // true

쓰기 지연 Transactional Write-behind

  • 하나의 트랜잭션에 여러 개의 INSERT or UPDATE SQL 문이 생성되는 경우, 1건씩 DB 연결하고 SQL 처리하는 것이 아니라, Buuffer 기능처럼 수집해놓고, 트랜잭션이 커밋되는 순간 DB 연결하고 SQL 를 처리한다.

영속성 컨텍스트의 특징 - 쓰기 지연

변경 감지 Dirty Checking

  • 영속성 상태의 엔티티의 프로퍼티 값을 변경하면, 다시 영속화 및 저장 처리하지 않더라도 UPDATE 문이 생성되며 DB 업데이터 처리한다.
Member member = em.find(Member.class, "memberA");
member.setName("KIM JIM");
...
// 해당 트랜잭션 커밋되는 순간, member 에 대한 UPDATE 문 처리

번외. Spring Data JPA 영속성 관리

  • Spring Data JPA 에서는 영속성을 트랜잭션 Transaction 단위로 관리가 된다.
  • Spring Data JPA 가 트랜잭션을 어떻게 관리가 되는지 알아보기 위해 7 가지 Case 를 정리하였다.

Question 01.

  • 기본 JPA 인터페이스를 통해 save() 함수 호출한다면, save() 인터페이스 함수 처리 직후 SQL 처리된다.
fun save() {
    println("======================= [ START ] =============================")
    val member = Member(name = "KIM JIM", age = 33)
    memberRepository.save(member)
    println("======================== [ END ] ==============================")
}

Output :
======================= [ START ] =============================
Hibernate: insert into members (age, name, id) values (?, ?, ?)
======================== [ END ] ==============================

Question 02.

  • @Transactional 애노테이션을 붙은 함수는 JPA 가 명시적으로 해당 함수 완전히 끝난 시점을 트랜잭션 커밋 시점으로 보고, 함수가 완전히 끝난 직후 SQL 처리된다.
@Transactional
fun save() {
    println("======================= [ START ] =============================")
    val member = Member(name = "KIM JIM", age = 33)
    memberRepository.save(member)
    println("======================== [ END ] ==============================")
}

Output :
======================= [ START ] =============================
======================== [ END ] ==============================
Hibernate: insert into members (age, name, id) values (?, ?, ?)

Question 03.

  • Question 01. 과 동일하게 @Transactional 없다면, JPA 인터페이스 함수 처리 직후 SQL 처리된다.
fun save() {
    println("======================= [ START ] =============================")
    val member = Member(name = "KIM JIM", age = 33)
    memberRepository.save(member)
    member.age = 34
    println("======================== [ END ] ==============================")
}

Output :
======================= [ START ] =============================
Hibernate: call next value for hibernate_sequence
Hibernate: insert into members (age, name, id) values (?, ?, ?)
======================== [ END ] ==============================

Question 04.

@Transactional
fun save() {
    println("======================= [ START ] =============================")
    val member = Member(name = "KIM JIM", age = 33)
    memberRepository.save(member)
    member.age = 34
    println("======================== [ END ] ==============================")
}

Output :
======================= [ START ] =============================
======================== [ END ] ==============================
Hibernate: insert into members (age, name, id) values (?, ?, ?)
Hibernate: update members set age=?, name=? where id=?

Question 05.

fun save(member: Member) = memberRepository.save(member)

fun save() {
    println("======================= [ START ] =============================")
    val member = Member(name = "KIM JIM", age = 33)
    save(member)
    member.age = 34
    println("======================== [ END ] ==============================")
}

Output :
======================= [ START ] =============================
Hibernate: insert into members (age, name, id) values (?, ?, ?)
======================== [ END ] ==============================

Question 06.

@Transactional
fun save(member: Member) = memberRepository.save(member)

fun save() {
    println("======================= [ START ] =============================")
    val member = Member(name = "KIM JIM", age = 33)
    save(member)
    member.age = 34
    println("======================== [ END ] ==============================")
}

Output :
======================= [ START ] =============================
Hibernate: insert into members (age, name, id) values (?, ?, ?)
======================== [ END ] ==============================

Question 07.

fun save(member: Member) = memberRepository.save(member)

@Transactional
fun save() {
    println("======================= [ START ] =============================")
    val member = Member(name = "KIM JIM", age = 33)
    save(member)
    member.age = 34
    println("======================== [ END ] ==============================")
}

Output :
======================= [ START ] =============================
======================== [ END ] ==============================
Hibernate: insert into members (age, name, id) values (?, ?, ?)
Hibernate: update members set age=?, name=? where id=?

출처