티스토리 뷰

반응형
[개요]
JPA를 공부하는데 가장 난해한 부분인 '영속성 컨텍스트'에 대해서 알아보겠습니다. 영속성 컨텍스트에 대한 지식이 수반되어야 JPA의 동작방식을 온전히 이해할 수 있습니다.
[영속성 컨텍스트 (Persistence Context)]
영속성 컨텍스트란 인스턴스로 존재하는 엔티티를 관리하고 영속화시키는 논리적 영역 입니다. 영속화의 사전적 의미는 '사라지지 않고 지속되게 한다' 입니다. 쉽게 말하자면 DB에 저장된다는 의미입니다.
하지만 '영속'이라는 말에 현혹되어서는 안됩니다. 영속성 컨텍스트에서 관리하는 엔티티라고 해도 반드시 '영속화'되어 DB에 저장된 것은 아닙니다. 다만 영속화 될 수 있는 가능성이 있을 뿐입니다.
영속성 컨텍스트는 Server와 Database 사이에 위치한다.
영속성 컨텍스트에서 엔티티를 관리하고 필요에 따라 DB의 데이터를 저장, 조회, 수정, 삭제할 수 있습니다. 이러한 작업을 담당하는 객체를 '엔티티 매니저 (Entity Manager)'라고 합니다.
영속성 컨텍스트는 크게 2가지 영역으로 나뉩니다.
영속성 컨텍스트는 크게 2가지 영역으로 나뉜다.
1차 캐시 저장소
영속성 컨텍스트가 관리하는 엔티티 정보를 보관합니다. 이 상태를 '영속 상태' 라고 합니다. 다시 한번 강조하지만 '영속 상태'는 아직 DB에 저장된 상태는 아닙니다. 단순히 영속성 컨텍스트에서 관리(managed)하는 상태일 뿐입니다.
쿼리문 저장소 (SQL 저장소)
JPA는 필요한 쿼리문(SQL)을 보관해둡니다. 최대한 여러 쿼리문을 모아두고, DB에 접근하는 횟수를 최소화하게되면 성능상에 이점을 얻을 수 있기 때문입니다. 저장해둔 쿼리문으로 DB에게 접근하는 행위는 엔티티 매니저의 '플러시 flush()'로 진행합니다.
[엔티티의 생명주기]
JPA(영속성 컨텍스트) 입장에서 엔티티의 생명주기를 4가지로 나눌 수 있습니다. (엔티티는 쉽게 말해 하나의 인스턴스, DB입장에서는 한건의 레코드정도로 이해하면 됩니다.)
1. 비영속(new, transient) 상태
엔티티가 영속성 컨텍스트와 전혀 관련이 없는 상태입니다.
영속성 컨텍스트와 관련이 없는 entity1
2. 영속(managed) 상태
엔티티가 영속성 컨텍스트에서 관리되고 있는 상태입니다. 또 한번 말하지만, 이는 아직 DB에 저장된 상태가 아닙니다. (왜 이렇게 강조하냐면 제가 이 부분 때문에 한동안 애를 먹었기 때문입니다. ㅠㅠ 키워드에 현혹되지 마세요.) 엔티티 매니저의 'persist()'를 사용하면 비영속 상태의 엔티티를 영속상태로 만들 수 있습니다.
persist를 이용하여서 entity1을 영속 상태로 만들 수 있다.
위의 이미지는 persist()를 실행한 이 후 영속성 컨텍스트를 도식화한 것입니다. 엔티티를 저장하는 INSERT 쿼리문이 생성 되었지만, 아직 DB에게 전달되지 않고 쿼리문 저장소에 보관되었습니다. 이 부분이 영속성 컨텍스트를 이해하는 핵심입니다. '플러시 flush()'가 실행되기 전에는 실제 DB에게 접근하지 않습니다.
계속 쌓이는 쿼리문들
여러개의 엔티티를 persist()하게 되더라도, 해당하는 INSERT 쿼리문은 계속 보관하게 됩니다.
flush로 쿼리문을 DB에 반영한다.
모아둔 쿼리문은 '플러시 flush()'를 실행하게 될 때 DB에 반영합니다. 플러시를 하더라도 1차 캐시 저장소에서 관리중인 엔티티들이 사라지는 것은 아닙니다. 플러시는 영속성 컨텍스트와 DB를 동기화(Synchronize) 할 뿐입니다.
 
생성한 엔티티를 입력할 때 이외에도 엔티티 매니저가 DB에서 조회해온 데이터도 '영속 상태'인 엔티티가 됩니다. 조회해온 데이터는 1차 캐시 저장소에 먼저 저장되고, 저장된 엔티티 정보를 반환합니다. 조회를 하기 위해서는 엔티티 매니저의 find()를 사용합니다.
DB에 데이터를 조회해서 1차 캐시 저장소에 '영속 상태'로 보관
만약 같은 엔티티를 한번 더 조회하게 되면 어떻게 될까요? JPA는 1차 캐시 저장소에 있는 엔티티를 반환하고 실제로 DB에게 접근은 하지 않습니다. JPA가 조회와 관련한 성능상의 큰 이점을 취할 수 있는 이유입니다. 또한 같은 인스턴스의 참조값을 반환하기 떄문에, == 로 동일성을 비교한다면 같은 인스턴스임을 확인 할 수 있습니다.
3. 준영속(detached) 상태

영속성 컨텍스트에서 관리되던 엔티티가 영속성 컨텍스트에서 관리되지 않게 되면, 이 엔티티를 준영속 상태라고 합니다.

엔티티를 준영속 상태로 만드는 방법은 3가지가 있습니다.

1.
특정 엔티티를 준영속 상태로 만들기 위해서는 엔티티 매니저의 detach()를 사용합니다.
특정 entity2만 준영속 상태로 만들 수 있다.
2. 영속성 컨텍스트 전체를 초기화 시키는 clear()를 사용할 수 있습니다. 이 때 쿼리문 저장소의 보관해둔 쿼리들도 모두 초기화됩니다.
영속성 컨텍스트를 초기화하면, 영속상태의 entity 전부 준영속 상태가 된다.
3. 영속성 컨텍스트를 닫아버리는 close()를 사용한다면, 영속성 컨텍스트 자체가 사라지게 되니, 관리되던 엔티티들은 모두 준영속 상태가 됩니다. (close()는 엄밀히 말해 엔티티 매니저가 닫히는 겁니다. 상황에 따라 다르지만 일단은 하나의 엔티티 매니저가 하나의 영속성 컨텍스트에 속한다고 생각하시면 됩니다.)
영속성 컨텍스트가 사라진다면, 자연스럽게 영속상태의 엔티티들이 준영속 상태로 변환된다.
- 준영속 상태의 엔티티는 엔티티매니저의 merge()를 사용하면 다시 영속성 컨텍스트에서 관리되는 '영속 상태'로 변환 할 수 있습니다.
4. 삭제(removed) 상태
삭제 상태는 엔티티를 영속성 컨텍스트에서 관리하지 않게 되고, 해당 엔티티를 DB에서 삭제하는 DELETE 쿼리문을 보관하게 됩니다. persist와 마찬가지로 '플러시 flush()'가 호출되기 전까지는 실제 DB에게 접근되지 않습니다.
1차 캐시 저장소에 보관된 정보는 바로 삭제되고, DELETE 쿼리문만 남는다.
[변경 감지 (Dirty Checking)]
엔티티의 생명주기를 설명하면서 INSERT, SELECT, DELETE를 어떤식으로 진행하게 되는지 담아보았습니다. 하지만 UPDATE와 관련한 엔티티 매니저의 메소드는 정의되어 있지 않습니다. 엔티티에 변경사항이 생긴 경우, 이에 대한 '변경을 감지' 할 뿐입니다.

사실 1차 캐시 저장소에는 본래 엔티티가 아니라, 엔티티에 대한 참조와 이 엔티티를 처음 영속 상태로 만들었을 때의 복사본(Snapshot)을 가지고 있습니다.(참고 : 1차캐시 저장소에서 영속성 컨텍스트는 엔티티를 엔티티의 @id 값으로 구별됩니다. 만약 같은 아이디를 가지는 다른 엔티티 인스턴스가 있더라도 같은 엔티티로 취급하게 됩니다.)

1차 캐시 저장소에는 엔티티의 참조값과 복사본(snapshot)이 담긴다.
'플러시 flush()'가 호출되고 실행하기 직전에, 엔티티 매니저는 복사본(Snapshot)과 실제 엔티티를 비교해봅니다. 만약 저장 해둔 복사본과 실제엔티티를 대조했을 때, 내용이 다르다면 (필드값이 다르다면) 엔티티 매니저는 '변경을 감지' 할 수 있습니다. 이 경우 적절한 UPDATE 문을 생성하고 플러시와 함께 쿼리문을 던져줍니다.
당연하게도 변경감지는 영속성 컨텍스트에서 관리하는 엔티티만을 대상으로 진행됩니다. 준영속 상태인 엔티티가 변경된다고 하더라도 변경감지는 발생하지 않습니다.
[더 공부해볼만한 주제들]
1.
스프링 데이터와 스프링 데이터 JPA - 실무에서 스프링 개발자가 엔티티 매니저를 직접 다루는 일은 거의 없습니다. 많은 경우 스프링 데이터 JPA를 사용하기 떄문입니다.
 
2.
플러시가 발생되는 시점
 
3.
스프링 프레임워크에서의 엔티티 매니저와 영속성 컨텍스트 - 스프링 프레임워크에서 같은 트랜잭션이라면 여러개의 엔티티매니저가 하나의 영속성 컨텍스트를 공유합니다. 이는 스프링의 정책입니다.
 
4.
LazyInitializationException - 준영속 상태인 엔티티로 지연 로딩 하게 되는 경우 발생하는 예외
 
5.
OSIV (Open Session In View) - 영속성 컨텍스트의 생존범위를 트랜잭션 범위가 아니라 스프링의 뷰(view)단까지 연장시키는 옵션. (부트에서는 OSIV가 기본적으로 적용된 상태입니다.)
 
6.
지연(lazy) 로딩과 즉시(eager) 로딩
 
7.
N+1문제와 예방, 해결 방법 (Fetch Join)
 
 
 
참고도서 - 자바 ORM 표준 JPA 프로그래밍 (저자 김영한님)
 
 
 
반응형
댓글