인프런 김영한님의 JPA 강의와 티스토리 글을 읽고 정리하였습니다.

엔티티 (매니저) 팩토리와 엔티티 매니저

  • 엔티티 매니저 팩토리
    • 엔티티 매니저를 만드는 공장
    • 엔티티 매니저 팩토리는 생성하는 비용이 크기 때문에 한 개만 만들어 전체 어플리케이션에서 공유한다.
    • 여러 스레드가 동시에 접근해도 안전하다.
  • 엔티티 매니저
    • 엔티티의 CRUD 등 엔티티와 관련된 모든 일 처리
    • 엔티티 매니저는 동시에 접근하면 안 되므로(동시성 문제) 스레드 간에 공유하지 않는다.
      • 동시성 문제: 두 개 이상의 작업이 공통된 자원에 접근하고자 할 때 발생하는 문제
        • "일관성 없는 읽기"와 "손실되는 업데이트" 문제가 있음.

 

영속성 컨텍스트란?

  • 엔티티를 영구 저장하는 환경

 

엔티티의 상태(생명 주기)

(사진 출처: 티스토리)

 

  • 비영속
    • 영속성 컨텍스트와 전혀 관계가 없는 상태
  • 영속
    • 영속성 컨텍스트에서 관리되는 상태
    • 엔티티 매니저가 엔티티를 영속성 컨텍스트에 저장하는 순간부터 영속 상태가 되어 영속성 컨텍스트에 의해 관리받는다.
    • 트랜잭션이 커밋되는 순간 영속성 컨텍스트의 엔티티들이 DB에 반영된다.
  • 준영속
    • 영속성 컨텍스트에 저장되었다가 분리되어 더이상 영속성 컨텍스트에 의해 관리되지 않는 상태
  • 삭제
    • 삭제된 상태

 

영속성 컨텍스트의 특징

  • 식별자(Identifier)
    • 영속성 컨텍스트는 엔티티를 식별자 값(@Id)으로 구분한다. 따라서 영속 상태의 엔티티는 반드시 식별자 값이 존재해야 한다.
  • Flush
    • JPA는 트랜잭션이 커밋되는 순간 영속성 컨텍스트에 저장된 엔티티를 DB에 반영하는 Flush(플러시)를 수행한다.

 

영속성 컨텍스트의 장점

  • 1차 캐시
    • 영속성 컨텍스트 내부에 캐시가 존재
    • 영속 상태의 엔티티들은 모두 이곳에 Map(key: @Id, value: 엔티티 인스턴스)으로 저장
    • 엔티티를 조회할 때 먼저 1차 캐시(메모리 상주)에서 찾고 캐시에 존재하지 않으면 DB를 조회한다.
    • 데이터베이스에 접근하지 않고 1차 캐시에 있는 엔티티를 반환하므로 데이터베이스에 접근하는 빈도를 줄일 수 있다.
    • 단, 하나의 트랜잭션 안에서만 효과가 있으므로 성능상 이점이 크지는 않다.
  • 동일성(Identity 보장)
    • 1차 캐시로 인해 조회 시 같은 엔티티 인스턴스를 반환하기 때문에 여러 번 조회하더라도 엔티티 인스턴스의 동일성을 보장한다.
  • 트랜잭션을 지원하는 쓰기 지연(Transactional write-behind)
    • 엔티티 매니저는 트랜잭션을 커밋하기 전까지 데이터베이스에 엔티티를 저장하지 않고 내부 쿼리 저장소에 INSERT SQL을 모아둔다.
    • 트랜잭션이 커밋될 때 모아둔 쿼리를 데이터베이스에 요청하여 반영한다.
      • transaction commit -> flush : 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 작업.
  • 변경 감지(Dirty Checking)
    • 엔티티의 변경 사항을 자동으로 감지하여 데이터베이스에 반영하는 기능이다.
    • 영속성 컨텍스트가 관리하는 영속 상태의 엔티티만 적용된다.
    • JPA는 엔티티를 영속성 컨텍스트에 보관할 때 최초 상태를 스냅샷으로 저장해둔다.
    • 트랜잭션 커밋하여 flush가 동작할 때 엔티티와 스냅샷을 비교하여 변경사항을 체크하고 수정 쿼리를 생성한다.
    • 수정 쿼리는 쓰기 지연 SQL 저장소로 보내지며 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 반영한다.
  • 지연 로딩(Lazy Loading)
    • 엔티티간 연관 관계가 맺어져 있는 엔티티를 검색할 때 연관 관계가 있는 엔티티는 프록시 데이터로 채우고 실제로 사용할 때 검색하게 하는 지연 로딩을 통해 불필요한 쿼리를 실행하지 않게 도와준다.
    • 지연 로딩을 사용하는 것이 어플리케이션 성능 향상에 도움을 준다.
    • 필요에 따라서는 연관 관계가 있는 엔티티와 join할 수도 있다.(ex. N+1 문제)

 

Flush 과정

  1. 변경 감지(Dirty Checking)가 동작해서 영속성 컨텍스트에 있는 모든 엔티티를 최초 스냅샷과 비교한다.
  2. 비교 후 수정된 엔티티에 대해 수정 쿼리를 생성하고 이는 쓰기 지연 SQL 저장소에 등록된다.
  3. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 반영한다.

 

Flush 방법

  • 직접 호출: em.flush()
  • 자동 호출
    • 트랜잭션 커밋 시점
    • JPQL 쿼리 실행
      • JPQL(Java Persistence Query Language): Java Persistence에서 지원하는 객체지향 쿼리
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기