JPA PersistContext
이 문서는 Hibernate ORM 공식문서를 읽으며 정리한 글입니다.
Hibernate.Session 과 EntityManager 의 API 는 데이터를 지속성있게 다룰 수 있도록 해주는데 이를 Persistence Context 라 한다.
Persistence Context 에서 관리되는 데이터는 기본적으로 database 와 persistence context 양쪽에서 연관된 상태로 관리된다.
Persistence data 는 아래와 같은 생명주기를 가진다.
LifeCycle
비영속 (transient) : 엔티티가 만들어졌지만 아직까지 Persistence Context 와 연관이 없는 상태를 뜻합니다.
위와 같이 Memer Entity 는 초기화가 됬지만, 아직 영속성 컨텍스트에 관리되지 않고있음을 의미합니다.
영속(managed / persistent) : 엔티티가 Persistence Context 와 연관된 상태를 뜻하며 Identifier 로 식별 가능한 상태를 뜻합니다. 데이터 베이스에 있거나 혹은 아직은 없을 수도 있습니다.
위와 같이 EntityManager 에 Persist 되는 순간 Persistence Context 에 관리됩니다.
비영속(detached) : Persistence Context 에서 관리되다가 분리된 것을 뜻합니다.
위와 같이 detach 됬을때는 dirtyChecking 이 안되는 것을 볼 수 있습니다.
즉, 영속성 컨텍스트에 의해 관리가 되지 않기 때문입니다.
아래 관리되고 있을때 예시를 한번 보시죠.
여기서는 정상적으로 update query 가 발생하는 것을 알 수 있습니다.
삭제 (removed) : 삭제 상태는 Persistence Context 에 식별자로 관리되고 있다가 삭제된 객체를 뜻합니다. 데이터 베이스에서 또한 삭제 됩니다.
Dirty Checking
Dirty Checking 은 Hibernate 에서 Persistence Context 에 의해 관리되는 Entity 가 변경됬는지 여부를 계산하는 것을 의미한다.
어떻게 Hibernate 는 이걸 알 수 있을까?
그건 바로 Hibernate 는 데이터베이스에서의 엔티티 상태에 대한 마지막 이력을 관리하고 있기 때문이다.
Persistence Context 를 플러시하는 과정에서 하이버 네이트는 관리되는 모든 엔티티에 대하여
"last known database state" 와 다른점이 없는지를 점검한다.
그래서 PC 에 의해 관리되는 많은 수의 엔티티가 있다면, 성능이 저하될 수도 있다. (처음 알았네..)
만약 DataType 의 내부 상태 변경에 신경쓰지 않는다면, Bytecode-EnhanceMent 로 Dirty Checking 를 강화할 수 있다고 한다.
Class 의 바이트 코드를 조작해서 엔티티에 직접 Dirty Checking 을 추적할 수 있다고 한다.
그래서 Flush 시간에 Hibernate 는 diff 를 계산할 필요없이 Entity 에세 변경점을 묻게 된다.
흠 Spring Data JPA 는 어떤걸 쓰고 있을꺄? 전자인가..?
일단 컴파일된 Entity 파일에 변경점을 질문할 수 있는 코드가 없는걸 봐서 전자일 거 같다는 생각이 든다.
아, 알아보니 이런 설정을 따로 해줘야 된다고 한다.
CascadeType
여기서는 기본적으로 CascadeType.ALL 에 대해서만 언급하려고 한다.
나머지가 궁금하면 미안한데 찾아보길 바란다 ㅠㅠ.. 글이 너무 길어질까봐 궁금한점만 정리하려고 한다.
일단 Entity 가 DB 에 올라가기 위해서는 PC 에 의해 관리되야 한다는 점을 깨달았을 것이다.
만약 아래처럼 1:N 관계가 있다고 해보자.
내가 원하는건 Team 의 List 에 Member 를 추가했을때 같이 DB 에 저장되게 하고 싶다.
그렇다면 Team 이 영속화될때 Member 또한 영속화 시켜야 할 것이다.
따라서 아래와 같은 옵션을 줘보자
아래와 같이 테스트를 해보면 잘 통과하는 모습을 확인할 수 있다.
그런데 궁금한 점이 하나 생겼다.
만약에 Team 안의 멤버를 변경해도 변경점을 찾을 수 있을까? 테스트를 해보자.
DB 에 이렇게 있다고 해보자.
결과가 어떨까?
실패하는 모습을 볼 수 있다. 왜일까?
CascadeType.REPLICATE 옵션이 없기 때문이다. 하지만 이것은 공개 옵션으로 따로 나와있지는 않다.
보면 알수 있듯이 ALL 을 쓰면 이를 사용할 수 있다.
그렇다면 옵션을 한번 바꿔보자.
UPDATE Query 가 나가면서 TEST 가 통과했다.