1. JPA 영속성 컨텍스트

영속성 컨텍스트(Persistence Context) : 엔티티(Entity)를 영속적으로 저장하는 환경

JPA 영속성 컨텍스트 : 엔티티의 생명주기를 관리하고, 애플리케이션과 데이터베이스 사이에서 수많은 최적화 작업을 해주는 논리적인 공간(메모리 공간)

→ JPA는 이 공간에서 엔티티를 관리하다가, 트랜잭션이 끝나면 최종 결과물만 DB에 반영

영속성 컨텍스트를 사용하는 이유

  1. 1차 캐시와 동일성 보장

    엔티티를 1차 캐시(임시 저장소)에 저장 → 이후에 동일한 id를 조회하면 DB를 조회하지 않고 캐시의 엔티티를 반환

    → 같은 트랜잭션에서 같은 id로 조회한 엔티티는 항상 동일한 메모리 주소의 인스턴스임을 보장

  2. 변경 감지(Dirty Checking) : 트랜잭션이 커밋되기 전 1차 캐시의 모든 엔티티를 최초 상태와 비교하고, 두 상태가 다르다면 자동으로 update SQL을 생성

  3. 쓰기 지연(Transactional Write-Behind) : SQL을 모아뒀다가 트랜잭션이 커밋되면 모든 SQL을 한 번에 DB로 보냄 → 네트워크 비용 감소, 성능 최적화

    <aside> 💡

    Flush : 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 것 (컨텍스트의 상태와 DB 상태를 맞추는 작업)

    → 트랜잭션을 커밋하는 것은 아님! 여전히 한 트랜잭션으로 관리되기 때문에 트랜잭션 롤백 시 Flush한 데이터도 롤백됨.

    </aside>

EntityManager

EntityManager : 애플리케이션과 데이터베이스 사이에서 엔티티 객체를 관리하고 실제 데이터베이스 작업을 수행하는 객체(모든 엔티티를 관리)

→ JPA Repository 내부에서 EntityManager를 사용하고 있음!

image.png

트랜잭션 전파(Transaction Propagation)

@Transactional : 메서드를 트랜잭션 안에서 실행

propagation 옵션을 통해 트랜잭션 안에 트랜잭션을 생성하는 등의 전략을 설정할 수 있음

  1. required(default) : 부모 트랜잭션이 있으면 그 트랜잭션에 참여, 없으면 새로운 트랜잭션 생성

  2. requires_new : 무조건 새로운 트랜잭션을 생성

    → 기능의 성공 여부와 상관없이 반드시 기록되어야 하는 기능 (ex: 사용자의 행동 로그, 에러 로그, 통계 데이터)

  3. nested : 부모 트랜잭션이 있으면 중첩된 트랜잭션 생성, 없으면 새 트랜잭션 생성

    → 하나의 큰 작업 중 실패할 가능성이 있는 부분을 분리할 때 유용 (ex: 여러 개 주문에 각각 쿠폰 처리)

    <aside> 💡

    requires_new vs. nested

    requiires_new : 부모와 별개의 트랜잭션이기에 부모가 롤백되어도 자식은 커밋될 수 있음

    nested : 부모 트랜잭션에 종속적이기에 자식만 롤백은 가능, 부모가 롤백되면 자식도 무조건 롤백

    </aside>

  4. supports : 부모 트랜잭션이 있으면 참여, 없으면 트랜잭션 없이 실행

    → 읽기 전용 메서드 처럼 트랜잭션이 있든 없든 로직 수행에 문제가 없고, 굳이 트랜잭션을 생성할 필요가 없을 때 적합(ex: 목록 조회)