변경감지
이번 글에서는 JPA의 변경감지 또는 더티체킹이라 불리는 개념에 대해 간략히 정리한다.
JPA 변경감지
JPQL에는 업데이트 문법이 존재하지 않는다. 그렇다면 JPA에서는 업데이트를 어떻게 구현해야할까?
JPA에서는 영속화된 Entity의 필드 값을 변경만 하기만 하면 자동으로 데이터베이스에 업데이트 되도록 반영이된다.
이것이 Dirty Checking이다.
더티체킹의 동작원리
- 영속성 컨텍스트가 엔티티가 영속화 되었을때 최초의 상태를 저장(스냅샷).
- 영속성 컨텍스트가 flush되는시점에 엔티티와 스냅샷 엔티티를 비교해 달라진 엔티티를 찾음.
- 이후 변경된 필드들을 이용하여 쓰기지연 SQL 저장소에 Update 쿼리를 생성하여 쌓음.
- 모든 작업이 끝나고 트랜잭션을 커밋을 하면 이때 쓰기지연SQL 저장소에 있는 쿼리들을 DB에 전달하여 Update.
여기서 조금 강조하고싶은 것은 "엔티티가 영속화 되었을때", 그리고 "트랜잭션 커밋을하면" 이라는 특징이다.
변경감지는 엔티티가 영속화 되있어야하며 트랜잭션에 묶여있어야한다.
예제를 보도록한다.
@GetMapping("/dirtyCheckingTest")
public List<Member> dirtyCheckingTest(){
Member memberOne = memberRepository.findOne(1L);
memberService.dirtyCheckingTest(memberOne);
List<Member> members = memberRepository.findAll();
memberOne.setName("updateName2");
return members;
}
@Transactional
public void dirtyCheckingTest(Member memberOne){
memberOne.setName("updateName");
}
먼저 userId가 1인 멤버엔티티를 jpql로 셀렉트하여 엔티티를 영속화한다.
그후 서비스단에서 해당 엔티티의 name필드를 변경한다.
그리고 다시 userId가 1인 멤버엔티티를 조회하여 리턴한다.
콘솔에 찍힌 쿼리를 보자.
로직에서 Update를 직접 날린적은 없지만 트랜잭션이 종료되면서 업데이트 쿼리가 발생한다.
여기까지가 더티체킹의 예제이며
그 아래로직 부터는 영속성컨텍스트의 원리를 복습하며 테스트해보기 위함이다.
자 업데이트 쿼리가 반영된후 모든 멤버 엔티티를 조회하고 리턴한다. 리턴하기전에 최초 조회한 엔티티객체의 name필드를 updateName2로 변경했다 하지만 리턴은 마지막에 다시 jpql로 셀렉트한 엔티티들이므로 기존 업데이트 쿼리가 반영되어 Id가1인 엔티티의 name 필드의 값이 UpdateName이라고 예상된다. 하지만 아니다. 결과는
updateName2이다.
이유는 이러하다. jpql로 조회한 엔티티의 식별자가 이미 영속성 컨텍스트에 존재한다면 db에서 조회한 결과를 버리고 영속성 컨텍스트에 있던 엔티티로 바꿔치기한다. 그렇기 때문에 리턴된 엔티티 리스트중 userid가1인 엔티티는 db에서 조회한 엔티티가 아니라 최초 조회한 memberOne오브젝트를 가리키고있다.