hibernate 코드로 이해하는 Spring Data JPA
Spring Data JPA(이하 스프링 JPA)는 Spring Data의 모듈로 JPA 기반의 데이터 접근 레이어(Data Access Layer)를 편하게 구현할 수 있도록 지원하는 프레임워크이다. 관계형 데이터베이스 기반의 데이터베이스를 활용해서 개발하는 스프링 프레임워크와 함께 널리 사용하는 모듈이다.
스프링 JPA는 Hibernate(이하 하이버네이트)의 기능을 스프링 프레임워크에서 맞게 확장한 모듈이다. 따라서 기본적인 구조와 동작은 하이버네이트 구현을 기반으로 하고 있다.
엔티티를 스프링 JPA의 JpaRepository.save() 메서드를 호출해서 저장한다고 해보자. 이때 하이버네이트가 어떻게 동작하는지 살펴보면서 하이버네이트의 원리를 이해하자.
설명과 함께 관련 코드도 링크로 첨부했으니 함께 살펴보길 권장한다.
시작에 앞서 JPA를 얘기할 때 자주 나오는 용어가 영속성 컨텍스트이다(Persistent Context). 흔히 1차 캐시라고 부르며 데이터베이스로부터 로드된/로드할 엔티티를 관리(Manage) 하고 캐싱(Caching)한다. EntityManager(이하 엔티티 매니저) 또는 Session(이하 세션)과 1:1 관계를 갖는다. Session은 EntityManager의 구현체이며 데이터베이스와 연결을 맺으며 생성된다.
Session/EntityManager의 구현체인 SessionImpl과 PersistentContext의 구현체 StatefulPersistenceContext를 기준으로 설명한다.
JpaRepository.save() 메서드를 호출해서 신규 생성한 엔티티를 저장하면 SessionImpl의 persist()가 호출된다(참고: Save는 Insert와 Update를 어떻게 구분할까)
하이버네이트는 이벤트(Event) 기반으로 동작한다. 각 이벤트마다 Event Listener Group이 존재하고 기본으로 등록된 리스너가 존재한다.
Persist 역시 이벤트로 발행되며 기본 리스너로 DefaultPersistEventListener가 등록돼있다. persist가 호출되면 AbstractEvent의 구현체인 PersistEvent를 넘겨(코드 1, 코드 2) PERSIST 이벤트 그룹의 DefaultPersistEventListener의 onPersist가 호출된다(코드). DefaultPersistEventListener에서 엔티티 영속화 작업이 동작한다. 영속화란 엔티티가 PersistentContext 내에서 관리되는 ManagedEntity가 되는 것으로 이해하면 쉽다(코드).
DefaultPersistEvent는 Event 유형에 따라 등록된 Callback도 함께 실행한다.
영속화된 엔티티는 EntityEntryContext에서 리스트 자료구조로 관리된다(코드). 가장 최근에 영속화된 Entity가 tail에 가장 먼저 영속화된 엔티티는 head에 위치한다(코드).
다음으로 신규 엔티티가 영속화되기 위해 데이터베이스 삽입(INSERT) 해야 한다. 이때 INSERT 명령이 바로 실행되지 않고 ActionQueue에 EntityInsertAction을 Enque 한다 (코드). 경우에 따라서 바로 실행되기도 한다. ActionQueue에는 INSERT 외에도 UPDATE, DELETE 등의 액션(EntityAction 구현체)이 담긴다(코드). 스프링 JPA의 지연 쓰기(Delayed Write)의 비밀이 바로 ActionQueue이다.
모든 영속화 작업이 끝났다면 Session의 flush가 호출된다(코드). FlushEvent를 Flush Event Group에 넘겨 DefaultFlushEventListener의 onFlush를 호출한다(코드 1, 코드 2). DefaultFlushEventListener는 ActionQueue의 executeActions를 실행하여 ActionQueue에 담긴 Action들을 일괄 실행한다(코드). Action의 execute 함수가 실행되며 Action의 종류에 따라 INSERT/UPDATE/DELETE 구문이 생성돼 실행된다(코드).
1) 데이터베이스와 연결하며 세션 또는 엔티티 매니저가 생성된다.
2) 신규 엔티티를 영속화(persist) 하는 이벤트를 발행한다.
3) 세션 내부의 영속성 컨텍스트에 관리받는 엔티티로 등록된다.
4) EntityInsertAction이 ActionQueue에 추가된다
5) Flush 이벤트를 발행한다
6) ActionQueue에 담긴 Action을 일괄 실행한다
7) INSERT 구문이 실행된다.