CS/JPA

Persistence Context

leah-only 2025. 6. 6. 17:28

🌿Persistence Context (영속성 컨텍스트)

Entity의 영속화에 관여하며 

Entity들이 DB로 바로 가지 않고 Entity를 저장하는 환경으로서의 역할을 한다. 

 

영속성 컨텍스트는 논리적인 개념이며 EntityManager를 통해서 영속성 컨텍스트에 접근하게 된다. 

@Repository
public class MemberRepository {

    @PersistenceContext
    private EntityManager em;

    public Long save(Member member) {
        em.persist(member);	// Entity 저장
        return member.getId();
    }

    public Member find(Long id) {
        return em.find(Member.class, id);
    }
}
  • EntityManager를 통해 Persistence Context에 접근
  • EntityManger의 persist 메서드를 통해 엔티티 저장
  • EntityManager와 Persistence Context는 N:1 관계

⏺️ Entity Manager Factory & Entity Manager

Entity Manager Factory

Entity Manager를 만들고 구성하는 법을 제공하는 인터페이스이다.

 

Entity Manager

DB table과 mapping된 객체인 Entity에 대한 CRUD 작업을 수행하기 위한 메서드를 제공하며 

Entity의 라이프 사이클과 영속성 관리 등을 담당

 

🔄 Entity 생명주기

  • New (비영속 상태)
    • 엔티티가 생성되었지만 EntityManager.persist()를 호출하지 않은 상태
    • 영속성 컨텍스트에 저장되지 않은 상태 & DB와 무관한 상태
  • Managed (영속 상태)
    • EntityManager.persist() 또는 EntityManager.find() 등에 의해 영속성 컨텍스트에 포함된 상태
    • 컨텍스트가 변경을 추적하며, flush 또는 commit 시점에 DB에 반영된
  • Detached (준영속 상태)
    • 영속성 컨텍스트에 의해 관리되다가 분리된 상태
    • ex. EntityManager.detach(), clear(), close() 호출 시
    • DB에는 아직 존재하나, 변경 감지가 더 이상 일어나지 않음
    • 필요 시 merge()를 통해 다시 영속 상태로 만들 수 있음
  • Removed (삭제된 상태)
    • 영속성 컨텍스트와 DB에서 제거된 상태 
    • 아직 flush나 commit 전이라면 영속성 컨텍스트에는 존재함

🗒️ 영속성 컨텍스트의 특징

✅ 식별자 값

  • 영속성 컨텍스트는 엔티티의 식별자(PK) 기준으로 엔티티를 구분하고 관리
  • 식별자가 없으면 영속화할 수 없고, 예외가 발생(@Id 없거나 null)

 

✅ 커밋 & 플러시

  • persist()로 영속성 컨텍스트에 저장한 엔티티는 트랜잭션이 커밋되어야 DB에 반영된다.
  • flush()는 영속성 컨텍스트의 변경사항을 쓰기 지연 SQL 버퍼에서 DB로 밀어넣는 작업. (DB에 반영하는 작업)

 

✅ 1차 캐시

  • 엔티티는 EntityManager 내부의 1차 캐시(Map 구조) 에 저장된다.
  • 동일 트랜잭션 내에서 식별자로 엔티티 조회 시 DB 접근하지 않고 캐시에서 바로 꺼냅니다.
  • 트랜잭션 커밋 시점에 1차 캐시는 소멸된다.

 

 

✅ 동일성 보장 (==)

  • 같은 트랜잭션 내에서 동일한 식별자로 조회된 엔티티는 같은 인스턴스이며, == 비교 시 true를 반환한다.
  • 이는 JPA에서 동일성(identity) 보장을 의미하며, 성능과 무결성 측면에서도 중요하다.

 

✅ 5. 쓰기 지연 (Write-behind)

  • persist()나 변경 감지로 인해 생성되는 SQL들은 쓰기 지연 SQL 버퍼에 임시로 저장되고,
    • 트랜잭션이 commit되거나 flush()가 호출될 때 실제 DB에 반영된다.
  • 이로 인해 여러 쿼리를 한꺼번에 모아서 처리할 수 있어 성능에 유리.

 

✅ 6. 변경 감지 (Dirty Checking)

  • 엔티티를 처음 영속화할 때 스냅샷(초기 상태) 을 저장
  • 트랜잭션 커밋 또는 flush() 시점에 현재 상태와 스냅샷을 비교하여 변경이 있을 경우 update 쿼리를 자동 생성
  • JPA는 setter 메서드나 필드 접근 등을 통해 변경 여부를 감지


❔flush() vs commit()

구분 설명
flush() 영속성 컨텍스트의 변경 내용을 쓰기 지연 SQL로 생성해서 DB에 전송 (쿼리 전송)
commit() 내부적으로 flush()를 호출한 뒤, 트랜잭션 종료하고 변경 내용을 확정 (persist) 

 

❔영속성 컨텍스트의 내용을 DB에 반영하려면?

  • flush() 메서드 직접 호출
  • 트랜잭션 commit()
  • JPQL 쿼리 실행
    • 자동으로 flush() 메서드 호출
    • 플러시 모드 설정 가능
      • FlushModeType.AUTO (기본) : 쿼리 실행 전 + 커밋 시 자동 flush
      • FlushModeType.COMMIT : 커밋 시에만 flush (쿼리 실행 전 flush X)

 

❔ JPQL 실행 시 왜 flush가 발생하나?

JPQL은 영속성 컨텍스트가 아닌 DB를 대상으로 실행된다. 

em.persist()만 호출한 후 아직 DB에 반영되지 않은 상태라면, JPQL로는 조회되지 않는다. 

따라서 JPA는 JPQL 실행 직전에 flush()를 자동으로 호출하여 쓰기 지연 SQL을 DB에 반영한 후 쿼리를 수행한다. 

이게 바로 FlushModeType.AUTO의 기본 동작이다. 


참고 : https://github.com/devSquad-study/2023-CS-Study/blob/main/JPA/jpa_persistence_context.md

https://colevelup.tistory.com/21