트랜잭션 안에서 Entity의 변경이 일어났을 때
변경한 내용을 자동으로 DB에 반영하는 것
ORM 구현체 개발 시 더티 체킹이라는 말을 자주 볼 수 있다.
더티 체킹
이 어떤 것을 뜻하는 지 간단히 살펴보자.
예를 들어 다음과 같은 코드가 있습니다.
(Spring Data Jpa가 익숙하시겠지만, 네이티브한 코드 먼저 보고 가겠습니다.)
@Slf4j
@RequiredArgsConstructor
@Service
public class PayService {
public void updateNative(Long id, String tradeNo) {
EntityManager em = entityManagerFactory.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin(); //트랜잭션 시작
Pay pay = em.find(Pay.class, id);
pay.changeTradeNo(tradeNo); // 엔티티만 변경
tx.commit(); //트랜잭션 커밋
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class PayServiceTest {
@Autowired
PayRepository payRepository;
@Autowired
PayService payService;
@After
public void tearDown() throws Exception {
payRepository.deleteAll();
}
@Test
public void 엔티티매니저로_확인() {
//given
Pay pay = payRepository.save(new Pay("test1", 100));
//when
String updateTradeNo = "test2";
payService.updateNative(pay.getId(), updateTradeNo);
//then
Pay saved = payRepository.findAll().get(0);
assertThat(saved.getTradeNo()).isEqualTo(updateTradeNo);
}
}
save 메소드
로 변경 사항을 저장하지 않았음에도 update 쿼리
가 실행되었습니다.
이유는 Dirty Checking
덕분인데요.
여기에서 Dirty란 상태의 변화가 생긴 정도로 이해하시면 됩니다. 즉, Dirty Checking이란 상태 변경 검사 입니다.
이때 변화가 있다의 기준은 최초 조회 상태입니다.
JPA에서는 엔티티를 조회하면 해당 엔티티의 조회 상태 그대로 스냅샷
을 만들어놓습니다.
그리고 트랜잭션이 끝나는 시점에는 이 스냅샷과 비교해서 다른점이 있다면 Update Query
를 데이터베이스로 전달합니다.
당연히 이런 상태 변경 검사의 대상은 영속성 컨텍스트가 관리하는 엔티티에만적용 됩니다.
Dirty Checking
대상에 포함되지 않습니다.@Transactional
이 함께 할 경우엔 다음과 같습니다.@Slf4j
@RequiredArgsConstructor
@Service
public class PayService {
private final PayRepository payRepository;
@Transactional
public void update(Long id, String tradeNo) {
Pay pay = payRepository.getOne(id);
pay.changeTradeNo(tradeNo);
}
}
@Test
public void SpringDataJpa로_확인() {
//given
Pay pay = payRepository.save(new Pay("test1", 100));
//when
String updateTradeNo = "test2";
payService.update(pay.getId(), updateTradeNo);
//then
Pay saved = payRepository.findAll().get(0);
assertThat(saved.getTradeNo()).isEqualTo(updateTradeNo);
}
아래와 같이 정상적으로 update 쿼리가 수행됨을 확인할 수 있습니다.
이런 경우엔 @DynamicUpdate
로 변경 필드만 반영되도록 할 수 있습니다.
엔티티 최상단에 아래와 같이 @DynamicUpdate
를 선언해주시면 됩니다.
@Getter
@NoArgsConstructor
@Entity
@DynamicUpdate // 변경한 필드만 대응
public class Pay {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String tradeNo;
private long amount;
변경분 (trade_no)만 Update 쿼리에 반영된 것을 확인할 수 있습니다.
https://coupa.ng/bg5M4j
https://github.com/gyoogle/tech-interview-for-developer/blob/master/Web/Spring/%5BSpring%20Data%20JPA%5D%20%EB%8D%94%ED%8B%B0%20%EC%B2%B4%ED%82%B9%20(Dirty%20Checking).md
https://jojoldu.tistory.com/415