김대용
JPA

엔티티 매니저와 영속성 컨텍스트를 정확하게 이해해보자

--------------------

코드를 보여주지 않는, 개념을 탐구하는 글이다. 김영한의 "자바 ORM 표준 JPA 프로그래밍" 책을 기반으로 공부했으나, 좀 더 정확한 개념 파악을 위해 공식 명세, 문서를 주로 찾아보고 정리했다.

JPA의 명세 내용을 기준으로 작성했고, 하이버네이트의 구현을 바탕으로 설명된 내용이 있으므로 참고하길 바란다.

엔티티

  • 비즈니스 요구사항을 모델링한 객체인 엔티티

엔티티 매니저

엔티티 매니저는 엔티티를 저장하고, 수정하고, 삭제하고, 조회하는 등 엔티티와 관련된 모든 일을 처리한다. 엔티티를 관리하는 가상의 데이터베이스로 생각하면 편하다. 엔티티 매니저는 영속성 컨텍스트와 1대1 또는 1대N 관계를 가진다.

언제 1대1이고 1대N 관계인가?

영속성 컨텍스트

JPA 명세에 따르면, 영속성 컨텍스트는 영속성 엔티티 인스턴스들의 모음이다.[1] 그리고 엔티티의 생명주기를 관리한다. 쉽게 말해, 영속성 컨텍스트엔티티의 CRUD를 담당하면서 저장했거나 불러온 엔티티를 기억하는 1차 캐시 역할을 한다.

엔티티 매니저와 영속성 컨텍스트엔티티 매니저와 영속성 컨텍스트

영속성 컨텍스트는 엔티티를 기억한다.

컨텍스트는 문맥이라는 뜻이다. 우리가 사람들이랑 대화를 할 때 한번 나온 얘기를 기억하고 다시 묻지 않는다. 이처럼 영속성 컨텍스트는 엔티티를 기억하는 것이라고 이해하면 편하다. 이때 영속성 컨텍스트는 1차 캐시로써 역할한다.

엔티티를 기억하므로 영속성 컨텍스트가 얻는 장점이 있다.

  • 캐싱 : 애플리케이션에서 동일한 엔티티를 여러번 조회시, DB를 거치지 않고도 기억하고 있던 엔티티를 돌려주는 점
  • 더티 체킹 : 캐싱된 엔티티를 활용해 애플리케이션에서 엔티티 변경시 기존과 바뀐 내용이 있는지 파악이 쉬운 점
  • 동일성 보장 : 캐싱 덕분에 같은 엔티티를 여러번 조회해도 동일성을 보장하는 점

영속성 컨텍스트는 한꺼번에 SQL을 전달한다.

엔티티 생성/수정/삭제를 위해선 트랜잭션을 시작해야한다. 애플리케이션은 엔티티 매니저에게 트랜잭션 시작과 엔티티 상태 변경(persist, remove 등)을 차례로 요청한다. 그러나 엔티티 매니저는 엔티티의 상태 변경사항을 데이터베이스에 바로 반영하지 않고, 영속성 컨텍스트에 따로 저장해둔다. 엔티티 매니저에 커밋을 요청해야 변경사항이 한꺼번에 SQL로 변환되어 데이터베이스에 보내지고 커밋된다.

이렇게 영속성 컨텍스트에 쌓인 엔티티 상태 변경사항을 데이터베이스와 동기화하는 절차플러시(flush)라고 한다.[2] 이런 전략은 쓰기 지연(write-behind)라고 하고, 이때 영속성 컨텍스트는 엔티티 상태 변경사항을 저장하는 트랜잭션 내 쓰기 지연 캐시로써 역할한다.[3] 플러시가 일어나는 조건은 위에 언급된 트랜잭션 커밋 외에도 여러 경우가 있다. 이는 엔티티의 생명주기를 다루면서 언급하겠다.

왜 플러시를 사용해 데이터베이스 쓰기를 지연시킬까? 물리적 데이터베이스 트랜잭션은 가능하면 할수록 최대한 짧아야한다. 긴 트랜잭션은 락 경합을 발생시켜 대용량 요청 처리에 불리하다.[4]

영속성 컨텍스트는 논리적 개념이다.

영속성 컨텍스트는 단순히 논리적 개념이다. 실존하지 않는다. 이 말이 헷갈릴 수도 있는데, 쉽게 비유하자면 정치이념과 사랑을 예로 들 수 있다. 보수주의, 진보주의, 사회주의, 공산주의 등 정치이념은 실존하지 않는다. 그러나 이는 정치인, 정당, 민중으로 표현될 수 있는 개념이다. 사랑은 실존하지 않는다. 그러나 언어적 표현, 육체적 표현 등으로 표현될 수 있는 개념이다.

실제로 JPA의 엔티티 매니저를 상속한 하이버네이트의 Session 인터페이스 설명을 보면, 이렇게 적혀져 있다. 영속성 컨텍스트의 "개념을 나타낸다"라는 표현을 사용한다.

Interface Session

Java 애플리케이션과 하이버네이트 사이의 주요 런타임 인터페이스다. 논리적 트랜잭션과 연관된 엔티티 인스턴스 집합인 영속성 컨텍스트의 개념을 나타낸다.

정리하자면, 트랜잭션 내에서 사용되는 엔티티를 기억하고 그 엔티티의 생명주기를 관리한다면 그건 뭐든 간에 영속성 컨텍스트라고 말할 수 있다. 그리고 엔티티 매니저는 영속성 컨텍스트를 사용할 수 있는 메서드를 제공하는 인터페이스이다.

개인적인 소감
Hibernate PersistenceContext 인터페이스

엔티티의 생명주기

엔티티는 영속성 컨텍스트에 의해 생명주기가 관리된다. 생명주기는 4가지 상태가 존재한다.[5]

  • 비영속 (new/transient) : 영속성 컨텍스트가 모르는 상태
  • 영속 (persist) : 영속성 컨텍스트에 저장되고, 기억되고 있는 상태
  • 준영속 (detached) : 영속 상태였다가 영속성 컨텍스트가 더 이상 기억하지 않는 상태
  • 삭제 (removed) : 영속성 컨텍스트에 엔티티가 삭제될거라고 기록한 상태. 즉, 삭제되는걸 기다리는 상태

즉, 영속성 컨텍스트가 기억하고 있는지 없는지로 나눌 수 있다.

  • 기억 있음 : 영속, 삭제
  • 기억 없음 : 비영속, 준영속

엔티티들의 생명주기를 관리하기 위해서는 엔티티 매니저가 제공하는 연산(메서드)을 사용한다. 어떤 연산을 사용해 각 상태로 전환할 수 있는지는 아래 그림을 참고하자.

엔티티 생명주기엔티티 생명주기

이 그림으로 알 수 있듯이, 영속성 컨텍스트와 관련 있는 영속, 삭제 상태는 플러시를 거쳐 데이터베이스와 동기화가 되는 대상이다.

생명주기의 설명은 여기서 이어 설명하겠다.

정리

  • 엔티티 : 비즈니스 요구사항을 모델링한 객체인 엔티티
  • 엔티티 매니저 : 영속성 컨텍스트를 조작할 수 있는 인터페이스
  • 영속성 컨텍스트
    • 영속성 컨텍스트의 역할
      1. 1차 캐시 : 영속한 엔티티를 기억한다
      2. 쓰기 지연 캐시 : 플러시를 하기 전까지 기억하고 있는 엔티티의 변경사항을 데이터베이스에 보내지 않는다
      3. 생명주기 관리 : 엔티티가 영속성 컨텍스트와 어떤 관련이 있는지에 따라 상태가 나뉜다
    • 영속성 컨텍스트의 장점
      1. 캐싱 : 애플리케이션에서 동일한 엔티티를 여러번 조회시, DB를 거치지 않고도 기억하고 있던 엔티티를 돌려준다
      2. 더티 체킹 : 캐싱된 엔티티를 활용해 애플리케이션에서 엔티티 변경시 기존과 바뀐 내용이 있는지 파악이 쉽다
      3. 동일성 보장 : 캐싱 덕분에 같은 엔티티를 여러번 조회해도 동일성을 보장한다
      4. 쓰기 지연 : 쓰기 지연 덕분에 트랜잭션을 사용하는 시간을 줄일 수 있다.
    • 영속성 컨텍스트는 논리적 개념 : 엔티티를 저장하고 그 라이프사이클을 관리한다면 뭐든 영속성 컨텍스트가 될 수 있다

참조

--------------------