Changsoon's Note Backend Developer

영속성 컨텍스트란?

영속성 컨텍스트란?

영속성 컨텍스트는 엔티티를 영구 저장하는 환경이라고 할 수 있다.

엔티티 매니저로 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

엔티티 매니저를 통해서 영속성 컨텍스트에 접근할 수 있고 영속성 컨텍스트를 관리할 수 있다.

영속은 뭘까?

엔티티는 4가지의 상태가 존재한다.

  • 비영속(new) : 영속성 컨텍스트와 전혀 관계가 없는 상태
  • 영속(managed) : 영속성 컨텍스트에 저장된 상태
  • 준영속(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 삭제(removed) : 삭제된 상태

즉, 비영속 상태의 엔티티를 영속성 컨텍스트에 저장한 상태를 영속이라고 한다.

영속성 컨텍스트는 왜 사용할까?

영속성 컨텍스트를 사용하면 이러한 장점들이 있다.

  • 1차 캐시
  • 동일성 보장
  • 트랜잭션을 지원하는 쓰기 지연
  • 변경 감지
  • 지연 로딩

1차 캐시 영속성 컨텍스트는 내부에 캐시를 가지고 있는데 이것을 1차 캐시라고 한다.

영속 상태의 엔티티는 모두 이곳에 저장된다.

엔티티를 지정할 때 @Id로 지정한 값이 식별자이고 값은 엔티티 인스턴스로 된 Map 형태로 저장된다.

예를 들어, em.find()를 호출하면 우선 1차 캐시에서 식별자 값으로 엔티티를 찾는다.

만약 찾는 엔티티가 있으면 데이터베이스를 조회하지 않고 메모리에 있는 1차 캐시에서 엔티티를 조회한다.

만약 1차 캐시에 엔티티가 없다면 엔티티 매니저는 데이터베이스를 조회해서 엔티티를 생성한다.

그리고 1차 캐시에 저장한 후 영속 상태의 엔티티를 반환한다.

동일성 보장

영속성 컨텍스트는 동일성을 보장한다.

예를 들어 em.find(Member.class, “member1”)를 반복해서 호출해도 영속성 컨텍스트는 1차 캐시에 있는 같은 엔티티 인스턴스를 반환한다.

트랜잭션을 지원하는 쓰기 지연

엔티티 매니저는 트랜잭션을 커밋하기 직전까지 데이터베이스에 엔티티를 저장하지 않고 내부 쿼리 저장소에 SQL을 차곡차곡 모아둔다.

그리고 트랜잭션을 커밋할 때 모아둔 쿼리를 데이터베이스에 보낸다.

변경 감지

JPA로 엔티티를 수정할 때는 단순히 엔티티를 조회해서 데이터만 변경하면 된다.

JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해둔다(스냅샷)

그리고 플러시 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾는다.

순서를 정리하면 다음과 같다.

  1. 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시가 호출된다.
  2. 엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾는다.
  3. 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보낸다.
  4. 쓰기 지연 저장소의 SQL을 데이터베이스에 보낸다.
  5. 데이터베이스 트랜잭션을 커밋한다.

플러시

플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.

영속성 컨텍스트를 플러시하는 방법은 3가지가 있다.

  • 직접 호출
    • 엔티티 매니저의 flush() 메서드를 직접 호출해서 영속성 컨텍스트를 강제로 플러시한다.
  • 트랜잭션 커밋 시 플러시 자동 호출
    • 트랜잭션을 커밋하기 전에 플러시를 호출해서 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영해야 한다.
    • 그렇기 때문에 JPA는 트랜잭션을 커밋할 때 플러시를 자동으로 호출한다.
  • JPQL 쿼리 실행 시 플러시 자동 호출
    • JPQL이나 CRITERIA 같은 객체지향 쿼리를 호출할 때도 플러시가 실행된다.

준영속이란?

영속성 컨텍스트가 관리하는 영속 상태의 엔티티가 영속성 컨텍스트에서 분리된 것을 준영속 상태라 한다.

따라서 준영속 상태는 영속성 컨텍스트가 제공하는 기능을 사용할 수 없다.

detach(), clear(), close() 메서드는 엔티티를 준영속 상태로 만든다.

준영속 상태의 엔티티를 다시 영속 상태로 변경하려면 merge() 메서드를 사용하면 된다.

merge()는 파라미터로 넘어온 준영속 엔티티를 사용해서 새롭게 병합된 영속 상태의 엔티티를 반환한다.

파라미터로 넘어온 엔티티는 병합 후에도 준영속 상태로 남아있다.