[ 김영한 자바 ORM 표준 JPA 프로그래밍 - 기본편 #3 ] 영속성 컨텍스트

김수호·2024년 5월 1일
0
post-thumbnail

✔️ 영속성 컨텍스트

  • JPA 를 이해하는데 가장 중요한 용어
  • "엔티티를 영구 저장하는 환경" 이라는 뜻
  • EntityManager 를 통해서 영속성 컨텍스트에 접근
    • EntityManager 를 생성하면, 그 안에 PersistenceContext(영속성 컨텍스트)라는 눈에 보이지 않는 논리적 개념의 공간이 생긴다.
  • EntityManager 를 통해 persist(..) 와 같은 작업을 한다고 해서 곧바로 INSERT 쿼리가 DB에 전달되지 않는다.
    • 해당 엔티티 객체는 영속성 컨텍스트의 관리 대상이 되는 상태인 영속 상태가 된다.

✔️ 엔티티의 생명주기

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

 

🤔 왜 이런 매커니즘이 있는걸까?
영속성 컨텍스트를 가짐으로 해서 생기는 이점이 있기 때문이다.

✔️ 영속성 컨텍스트의 이점

  • 엔티티 조회 - 1차 캐시
  • 엔티티 조회 - 동일성(identity) 보장
  • 엔티티 등록 - 트랜잭션을 지원하는 쓰기 지연(Transactional write-behind)
  • 엔티티 수정 - 변경 감지(Dirty Checking)
  • 지연 로딩(Lazy Loading)
    • 지연 로딩은 이후에 학습한다.

 

👉 하나씩 알아보자.

  • 엔티티 조회 - 1차 캐시
    • 영속성 컨텍스트는 내부에 1차 캐시라는 것을 들고있다. (Map 형태)
      • 1차 캐시 내부에는 ( 엔티티 객체의 PK 정보, 엔티티 객체, 최초 1차 캐시에 들어온 시점의 엔티티 정보 ) 등이 있다.
    • 이렇게 되면, 조회시 이점이 있다.
      • em.find(..) 를 통해 조회시, JPA는 영속성 컨텍스트 내부 1차 캐시 영역을 조회한다.
        • 1차 캐시에 값이 존재하면, 캐시에 있는 값으로 조회한다.
        • 1차 캐시에 값이 없으면, DB에서 조회를 하고, 조회된 값을 1차 캐시에 저장한다. 그리고 그 저장된 값을 반환한다.
      • 참고) EntityManager 는 클라이언트 요청에 해당하는 트랜잭션 종료시 close 된다. 따라서, 1차 캐시는 하나의 트랜잭션 안에서만 적용된다.
  • 엔티티 조회 - 동일성(identity) 보장
    • 한 트랜잭션 안에서 동일한 객체를 조회할 경우 반환되는 객체(findMember1, findMember2)가 서로 같은 것(==)을 말한다. ( 1차 캐시에서 조회하기 때문에 가능한 것이다. )
    • 참고) 1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공
  • 엔티티 등록 - 트랜잭션을 지원하는 쓰기 지연
    • 영속성 컨텍스트 안에는 1차 캐시 외에도 쓰기 지연 SQL 저장소라는 것이 존재한다.
      • em.persist(memberA); 라고 하면, 먼저 memberA 가 1차 캐시에 들어간다. 그러면서 JPA는 memberA 엔티티를 분석해서 INSERT SQL를 생성한다. 그리고 쓰기 지연 SQL 저장소라는 곳에 쌓아둔다.
      • em.persist(memberB); 라고 하면, 먼저 memberB 가 1차 캐시에 들어간다. 그러면서 JPA는 memberB 엔티티를 분석해서 INSERT SQL를 생성한다. 그리고 쓰기 지연 SQL 저장소라는 곳에 쌓아둔다.
      • tx.commit(); 을 통해 트랜잭션을 커밋하는 시점에, 쓰기 지연 SQL 저장소에 쌓인 쿼리가 데이터베이스에 전송된다. 그리고 트랜잭션이 커밋된다.
  • 엔티티 수정 - 변경 감지(Dirty Checking)
    • 엔티티의 값을 변경하기 위해서는 em.update(..) 같은 코드가 있어야할 것 같지만, 마치 자바 컬렉션에서 값을 변경하는 것 처럼, 그런 코드가 없어도 DB에 값이 변경된다. 어떻게 가능할까? 비밀은 영속성 컨텍스트에 있다.
    • 트랜잭션이 커밋되는 시점에 내부적으로 JPA 는 다음과 같이 동작한다.
      • 트랜잭션이 커밋되는 시점에 내부적으로 flush() 가 호출된다.
      • 플러시 발생시, 1차 캐시 내부에 엔티티와 스냅샷(최초로 1차 캐시에 값이 들어온 시점의 상태)을 비교한다. (변경 감지)
      • 비교한 값이 일치하지 않은 경우(=변경된 경우), UPDATE SQL 을 생성하여 쓰기 지연 SQL 저장소에 만들어둔다. 그리고 이를 데이터베이스에 전달하여 반영하고 커밋한다.
    • 참고) 결과 로그

 

✔️ 플러시

  • 영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는 것을 말한다.
  • 트랜잭션이 커밋되면 플러시가 자동으로 발생한다.
  • 플러시 발생시 일어나는 일들
    • 변경 감지(Dirty Checking)
    • 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록
    • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송
      • 참고) 쓰기 지연 SQL 저장소에는 등록, 수정, 삭제 쿼리 등이 담겨있다.
  • 영속성 컨텍스트를 플러시하는 방법
    • em.flush() : 직접 호출
    • 트랜잭션 커밋 : 플러시 자동 호출
    • JPQL 쿼리 실행 : 플러시 자동 호출
  • 참고) flush() 가 발생한다고 해서 데이터베이스 트랜잭션이 커밋되는 것은 아니다.
  • 참고) flush() 가 발생한다고 해서 1차 캐시가 지워지지 않는다. ( 1차 캐시에는 아무런 영향을 주지 않는다. )
  • 참고) 플러시 모드 옵션
    • FlushMode.AUTO : 커밋이나 쿼리를 실행할 때 플러시 (기본값)
    • FlushMode.COMMIT : 커밋할 때만 플러시

 

✔️ 준영속 상태

  • 영속 -> 준영속
  • 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)
  • 영속성 컨텍스트가 제공하는 기능(Dirty Checking, .. 등등)을 사용 못함
  • 준영속 상태로 만드는 방법
    • em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
    • em.clear() : 영속성 컨텍스트를 완전히 초기화
    • em.close() : 영속성 컨텍스트를 종료

강의를 듣고 정리한 글입니다. 코드와 그림 등의 출처는 김영한 강사님께 있습니다.

profile
현실에서 한 발자국

0개의 댓글