엔티티의 변경 사항을 데이터베이스에 자동으로 반영하는 기능을 변경 감지, Dirty Checking이라고 한다.
나는 이번 에코노베이션 DEV의 프로젝트를 개발하면서, 엔티티 수정을 Dirty Cheking 방식으로 개발하였는데, 이 때 궁금한 점들이 발생했다.
내가 가졌던 궁금한 점은 코드를 보면서 자세히 설명을 해보겠다.
UpdateMentoInfoHandler.class
@Component
@RequiredArgsConstructor
public class UpdateMentoInfoHandler {
private final MentoService mentoService;
@Transactional
public void execute(MentoInfoUpdateRequestDTO mentoInfoUpdateRequestDTO, Long Id) throws IOException {
mentoService.updateMentoInfo(mentoInfoUpdateRequestDTO, mentoService.findMento(Id));
}
}
MentoService.class
@Transactional
public void updateMentoInfo(MentoInfoUpdateRequestDTO updateRequest, Mento mento) {
if (updateRequest.getIntroduction() != null) {
mento.updateIntroduction(updateRequest.getIntroduction());
}
if (updateRequest.getCompany() != null) {
mento.updateCompany(updateRequest.getCompany());
}
if (updateRequest.getMajor() != null) {
Major.getMajor(updateRequest.getMajor().toString());
mento.updateMajor(updateRequest.getMajor());
}
}
이 때 궁금한 점은 UpdateMentoInfoHandler
는 JPA를 통한 조회인 em.find()가 구현된 mentoService.findMento(Id) 코드를 사용하기에, Mento 엔티티가 영속 상태인 것이 이해가 되지만, MentoService
가 인자로 받은 Mento 엔티티는 영속 상태가 아닐텐데 어떻게 더티체킹이 되는건지에 대해서였다.
일단 더티 체킹이 일어나는 환경은 아래 두 가지 조건이 충족되어야 한다.
@Transactional
) 안에서 엔티티를 변경하는 경우2번 조건이 충족되었지만, UpdateMentoInfoHandler
와 MentoService
는 다른 트랜잭션이기에 다른 영속성 컨텍스트를 가지므로, UpdateMentoInfoHandler
에서 em.find()를 통해 영속성 컨텍스트에 Mento를 저장하더라도 MentoService
의 영속성 컨텍스트에는 존재하지 않는다.
그런데 도대체 어떻게 Mento가 영속성에 있을 수 있을까?
나는 이 궁금증을 해결 하기 위해 2가지 가설을 세워보았다.
영속 상태의 엔티티를 인자로 넘기면 영속 상태가 유지가 된다.
MentoService.updateMentoInfo()
와 UpdateMentoInfoHandler.execute()
가 같은 트랜잭션 범위라 같은 영속성 컨텍스트를 공유하는거 아닐까?
2번이 왜 정답인지를 설명해보겠다.
먼저 현재 코드에서는 MentoService.updateMentoInfo()
와 UpdateMentoInfoHandler.execute()
모두 @Transactional이 명시적으로 선언 되어있고, MentoService.updateMentoInfo()
에서 UpdateMentoInfoHandler.execute()
를 호출하였기에, 부모와 자식 관계의 트랜잭션이라고 할 수 있다.
그리고, 현재 @Transactional의 Propagation 기본 설정은 REQUIRED인데, REQUIRED는 부모 트랜잭션 내부에 자식 트랜잭션이 실행되는 경우, 부모 트랜잭션이 자식 트랜잭션이 합류한다.
그렇기에 부모 트랜잭션의 영속성 컨텍스트와 자식 트랜잭션의 영속성 컨텍스트에 동일한 Mento 엔티티가 존재했기에 더티 체킹이 가능했던 것이었다!!!
궁금증이 해결되어서 즐거운 마음으로 있었으나, 또 다른 궁금증이 생겨서 부모 트랜잭션에 있는 @Transactional
어노테이션을 제거 해보았다.
@Component
@RequiredArgsConstructor
public class UpdateMentoInfoHandler {
private final MentoService mentoService;
public void execute(MentoInfoUpdateRequestDTO mentoInfoUpdateRequestDTO, Long Id) throws IOException {
mentoService.updateMentoInfo(mentoInfoUpdateRequestDTO, mentoService.findMento(Id));
}
}
@Transactional
public void updateMentoInfo(MentoInfoUpdateRequestDTO updateRequest, Mento mento) {
if (updateRequest.getIntroduction() != null) {
mento.updateIntroduction(updateRequest.getIntroduction());
}
if (updateRequest.getCompany() != null) {
mento.updateCompany(updateRequest.getCompany());
}
if (updateRequest.getMajor() != null) {
Major.getMajor(updateRequest.getMajor().toString());
mento.updateMajor(updateRequest.getMajor());
}
}
나는 부모 트랜잭션을 제거하면, 같은 영속성 컨텍스트를 더 이상 공유하지 않기에 부모 메서드에서 영속성 컨텍스트에 저장했던 Mento 엔티티가 더 이상 Dirty Checking이 되지 않을 것이라고 생각했다…
그러나 여전히 Dirty Checking이 되는걸 보고 충격을 금치 못했다…
어떻게 여전히 Dirty Checking이 되는지를 알아보도록 하자!!
다음 포스팅에서 계속!!!