일단 영속성 컨텍스트는 엔티티를 영구 저장하는 환경으로 1차 캐싱, 쓰기 지연, 변경 감지를 통해 영속 로직을 효율적으로 할 수 있게 해줍니다
그리고 엔티티 매니저는 이 영속성 컨텍스트를 관리하는 작업을 도와주는 역할로, 영속성 컨텍스트와 상호작용함으로서 영속 로직을 수행하는 역할을 가지고 있습니다
엔티티 매니저에 의해 관리되는 엔티티는 영속성 컨테이너에 대해 세 가지 상태를 갖습니다
비영속
엔티티 객체가 새로 생성되었지만 아직 영속성 컨텍스트와 연관되지 않은 상태입니다
public void create(String id, String name) {
Study study = new Study(id, name);
}
이렇게 그냥 객체가 하나 생성됐다 보시면 됩니다
영속
엔티티 객체가 영속성 컨텍스트에 의해 관리되고 있는 상태로, 엔티티의 변경 사항이 자동으로 DB에 반영됩니다
이걸 좀 변경 감지라 합니다
@Transactional
public void create(String id, String name) {
Study study = new Study(id, name);
System.out.println("study persist status : " + em.contains(study));
em.persist(study);
System.out.println("study persist status : " + em.contains(study));
}
실제로 위 코드 실행 시
study persist status : false
study persist status : true
비영속 상태에는 영속성 컨텍스트(em)에 포함되어 있지 않고 영속(persist) 해줄 시 영속성 컨텍스트에 포함됩니다
준영속
엔티티 객체가 영속성 컨텍스트에 의해 관리되었다가 현재는 관리되지 않는 상태로, 더이상 엔티티의 변경 사항이 DB에 반영되지 않습니다
즉 변경감지가 일어나지 않습니다
영속성 컨텍스트 종료, 트랜잭션 종료 등으로 준영속 상태로 전환됩니다
// StudyService.java
public void study(String id, String newName) {
Study study = study2Service.getStudy(id);
study2Service.changeStudy(study, newName);
}
// Study2Service.java
@Transactional(readOnly = true)
public Study getStudy(String id) {
return studyRepository.findById(id).get();
}
@Transactional
public void changeStudy(Study study, String newName) {
System.out.println("study is persist status : " + em.contains(study));
study.setName(newName);
}
실제로 위와 같은 코드에서 study 객체를 조회하여 영속 상태였음에도 불구하고 getStudy의 트랜잭션이 끝나면서 study 객체가 준영속 상태로 변하면서 changeStudy에서 변경한 정보가 반영되지 않게 됩니다
Hibernate:
select
s1_0.id,
s1_0.name
from
study s1_0
where
s1_0.id=?
study is persist status : false
실제로 영속성 컨테이너에도 포함되어 있지 않고 update 쿼리도 날라가지 않습니다
그럼 이를 해결할 수 있는 방법은 뭐가 있을까요?
첫 번째로 study에 트랜잭션을 걸어주는 겁니다
하나의 큰 트랜잭션이 걸리며 해당 트랜잭션 안에서 study는 영속성 컨텍스트에서 관리됩니다
두 번째로 study를 다시 영속화 시켜주는 겁니다
merge 메서드를 통해 준영속 엔티티를 다시 영속화 시킬 수 있습니다
@Transactional
public void changeStudy(Study study, String newName) {
em.merge(study);
System.out.println("study is persist status : " + em.contains(study));
study.setName(newName);
}
이렇게 영속화를 시키면
Hibernate:
select
s1_0.id,
s1_0.name
from
study s1_0
where
s1_0.id=?
Hibernate:
select
s1_0.id,
s1_0.name
from
study s1_0
where
s1_0.id=?
study is persist status : false
다시 DB에서 조회를 해서 영속화를...
엥 근데 왜 persis status가 false죠? 영속화가 안됐다는데요?
그 이유는 merge 시 객체가 영속성 컨텍스트에 등록되며 새로운 객체가 반환되기 때문에 그렇습니다
진짜 그럴까요?
@Transactional
public void changeStudy(Study study, String newName) {
Study mergedStudy = em.merge(study);
System.out.println("study is persist status : " + em.contains(study));
System.out.println("mergedStudy is persist status : " + em.contains(mergedStudy));
System.out.println("study = " + study);
System.out.println("mergedStudy = " + mergedStudy);
mergedStudy.setName(newName);
}
이렇게 merge가 반환하는 객체에 대해 상태와 각 객체 study, mergedStudy를 출력하고 mergedStudy의 name을 바꿔보겠습니다
Hibernate:
select
s1_0.id,
s1_0.name
from
study s1_0
where
s1_0.id=?
Hibernate:
select
s1_0.id,
s1_0.name
from
study s1_0
where
s1_0.id=?
study is persist status : false
mergedStudy is persist status : true
study = knu_chatbot.entity.Study@12680685
mergedStudy = knu_chatbot.entity.Study@4eb168a1
Hibernate:
/* update
for knu_chatbot.entity.Study */update study
set
name=?
where
id=?
그럼 앞의 경우와는 다르게
1. mergedStudy의 상태가 영속으로 변함
2. 변경 감지에 의해 mergedStudy의 name이 update 되었음
을 알 수 있고 실제로 study와 mergedStudy를 출력해보면 서로 다른 객체(Study@12680685, Study@4eb168a1)를 나타내고 있음을 알 수 있습니다