영속성 컨텍스트

랏 뜨·2024년 12월 16일

🔎 Overview

  이전 포스팅 중 Cascade트랜잭션에 관해서 다룬 적이 있다.
이 둘을 포함하여 JPA를 공부하면서, 영속성 컨텍스트에 대한 개념이 많이 부족하다는 것을 느꼈다.

  어렴풋이는 알고 있지만, JPA를 수월하게 다루기 위해서는 이 영속성 컨텍스트에 대한 개념은 확실하게 이해를 해야 한다고 느껴졌기에, 이번에는 이를 주제로 공부하고 기록하려고 한다.


📕 영속성 컨텍스트

  • 엔티티를 저장하는 논리적인 저장소
  • JPA 에서 EntityManager영속성 컨텍스트를 관리
  • JPADB 와 직접적으로 상호작용하지 않고, 영속성 컨텍스트를 통해 엔티티의 상태를 관리


✔️ 엔티티의 상태

1) 비영속 상태

  • 영속성 컨텍스트에 의해 관리되지 않는 상태

2) 준영속 상태

  • 영속성 컨텍스트에 의해 관리되었다가, 더 이상 관리되지 않는 상태
  • 영속화된 적이 있는 상태

3) 영속 상태

  • 영속성 컨텍스트에 의해 관리되는 중인 상태

📸 스냅샷

  • 엔티티가 처음 영속화 됐을 시점의 상태 (초기 상태)
  • JPADirty Checking을 구현하기 위한 핵심 메커니즘
  • 영속성 컨텍스트의 메모리를 차지하므로, 대량 데이터 처리 시에는 준영속화 등을 통해 메모리 절약이 필요


➡️ 스냅샷을 이용하는 동작 원리 (Dirty Checking)

1) 엔티티영속화되면 초기 상태 저장

  • EntityManagerpersist()DB 를 통해 값을 직접적으로 가져오면, 해당 엔티티는 영속화되며 스냅샷으로 저장됨
  • 스냅샷은 원본 상태의 값으로, 영속성 컨텍스트의 내부 메모리에서 따로 관리

2) 트랜잭션 내에서 엔티티의 상태 변화 추적

  • 스냅샷은 일반적으로 한 번 등록되면 불변
  • 스냅샷이 변하는 예외 상황
    • EntityManager 의 메서드 직접 호출
      • flush() -> 호출 시점에서 영속성 컨텍스트의 변경 내용스냅샷으로 변경
      • refresh() -> 호출 시점에서 DB 의 값으로 스냅샷 변경 (일종의 rollback)
      • merge() -> 준영속 상태에서 다시 영속화를 진행하면서 스냅샷 변경
  • Dirty Checking 을 통해, 스냅샷과 현재의 엔티티 상태가 다르면 UPDATE 쿼리 자동 생성

3) 트랜잭션 커밋 시

  • 엔티티의 상태 변경이 있으면 UPDATE 쿼리를 통해 DB 에 자동 반영
  • 엔티티의 상태가 스냅샷과 동일하면 쿼리를 실행하지 않음

📑 영속성 컨텍스트의 특징

1) 1차 캐시

  • DB 작업을 통해 가져온 엔티티를 1차 캐시에 저장
  • 동일한 트랜잭션 내에서의 해당 엔티티 참조는 DB 를 통하지 않고 이 1차 캐시에서 실행
  • 즉, 해당 엔티티는 동일한 트랜잭션 내에서는 영속성 컨텍스트라는 메모리에서 관리
  • 메모리에서 데이터를 직접 가져오므로 성능 향상
  • 트랜잭션 커밋 직전 1차 캐시값과 스냅샷의 비교를 통해 Dirty Checking 진행

2) 쓰기 지연

  • 동일한 트랜잭션 내의 영속성 컨텍스트의 엔티티 값을 수정해도 DB 에 바로 반영되는 것은 아님
  • 필요 없을 때까지 수정 작업을 진행한 후, 마지막 한 번만 DB 와 작업할 수 있음
  • 즉, 여러 SQL 쿼리를 하나의 Batch 로 처리하여 성능 향상 가능

3) Dirty Checking

  • 스냅샷과 엔티티의 상태 변화를 비교한 후, 다르면 자동으로 DBUPDATE

4) 동일성 보장

  • 동일한 트랜잭션 내에서 같은 엔티티를 조회하면 동일한 객체를 반환
  • 이는 1차 캐시를 통해 보장

🏴‍☠️ 영속성 컨텍스트의 전파

  • 한 트랜잭션이 다른 트랜잭션에 영향을 미치는 방식을 결정
  • @Transactional(propagation = ?) 으로 전파 방식 결정

⭐ 전파 방식(속성)

1) Propagation.REQUIRED

  • 기존 트랜잭션이 있으면, 그 트랜잭션을 사용
  • 기존 트랜잭션이 없으면, 새로운 트랜잭션 시작
  • Propagation을 설정하지 않았을 때의 기본값
@Transactional
public void do() {}

2) Propagation.REQUIRES_NEW

  • 항상 새로운 트랜잭션으로 시작
  • 기존 트랜잭션이 존재하면 잠시 보류
  • 새로운 트랜잭션은 독립적으로 존재, 부모 트랜잭션에 영향을 미치지 않음
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void do() {}

3) Propagation.NESTED

  • 현재 트랜잭션 내에서 중첩된 트랜잭션 시작
  • 부모 트랜잭션의 일부로 처리되므로, 항상 부모 트랜잭션의 상태와 함께 관리
  • 부모 트랜잭션이 롤백 혹은 커밋 되면 동일하게 적용
@Transactional(propagation = Propagation.NESTED)
public void do() {}

4) Propagation.SUPPORTS

  • 기존 트랜잭션이 존재하면, 그 트랜잭션 사용
  • 기존 트랜잭션이 존재하지 않으면, 트랜잭션 없이 실행
@Transactional(propagation = Propagation.SUPPROTS)
public void do() {}

5) Propagation.NOT_SUPPROTED

  • 기존 트랜잭션 일시 중지 후 실행
  • 트랜잭션 안에서, 트랜잭션 작업을 하면 안 되는 부분에 사용
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void do() {}

6) Propagation.MANDATORY

  • 기존 트랜잭션이 존재하면, 그 트랜잭션 사용
  • 기존 트랜잭션이 존재하지 않으면, 예외 발생
  • 트랜잭션을 반드시 사용하도록 요구
@Transactional(propagation = Propagation.MANDATORY)
public void do() {}

7) Propagation.NEVER

  • 기존 트랜잭션이 존재하면, 예외를 발생
  • 트랜잭션 없이 실행해야 할 경우에 사용
@Transactional(propagation = Propagation.NEVER)
public void do() {}

‼️ 영속성 컨텍스트의 트랜잭션 전파를 잘 사용하면, 다중 트랜잭션이 동시에 존재하거나 상호작용할 때 성능의 향상을 기대할 수 있음


참고) OpenAI. (2024).ChatGPT(4o)[Large language model].https://chatgpt.com/

profile
기록

0개의 댓글