개발 중 JPA의 쿼리 동작을 보면서, 나는 몇 가지 이상한 점을 겪었다.
INSERT만 한 줄 알았는데 UPDATE가 나가고, INSERT는 찍혔는데 DB에는 없고…
내가 잘못 알고 있었던 점과 JPA가 실제로 동작하는 방식을 정리한 것이다.
@Transactional
public boolean a(Eco2 eco2) {
Eco2 save = eco2Repository.save(eco2); // INSERT
b(save.getId());
return true;
}
public boolean b(Long id) {
Eco2 eco2 = eco2Repository.findById(id).get(); // SELECT?
eco2.update(LocalDate.now()); // Dirty Checking → UPDATE 예약
return true;
}
1. 트랜잭션 안에서 객체가 변경되면 INSERT만 나간다?
1.1 기존 이해
트랜잭션 안에서 save()로 영속화한 객체를 수정하면,
JPA는 그 변경 내용을 INSERT 쿼리 하나에 반영해서 한번에 날릴 줄 알았다.
save(entity);
entity.setSomething(...);
이렇게하면 insert 하나만 나간다고 생각했다.
1.2 실제 동작
INSERT는 save() 시점의 상태로만 준비되고,
이후 setX() 같은 필드 변경은 Dirty Checking에 의해 UPDATE로 따로 예약됨.
- save() → INSERT 예약
- setSomething() → UPDATE 예약
- flush() 시점에 → INSERT + UPDATE 순으로 나감
결과적으로 쿼리는 2개 나간다.
2. IDENTITY 전략은 save() 해도 아직 DB에는 안 들어가겠지?
2.1 기존 이해
save(entity); // 로그에는 insert 쿼리 찍힘
DB에는 값이 없음
2.2 실제 동작
IDENTITY 전략은 ID 값을 DB가 만들어야 하므로, save() 하는 순간 INSERT가 즉시 나감.
하지만 트랜잭션 커밋 전이기 때문에, 실제 DB에는 반영되지 않음.
MySQL에서는 autocommit=false 상태에서 insert → commit 안 하면
다른 커넥션에서는 해당 row가 보이지 않음.
3. SEQUENCE 전략은 INSERT 시점에 쿼리가 나간다?
3.1 기존 이해
save() 하면 insert도 같이 나가겠지?
3.2 실제 동작
SEQUENCE 전략은 먼저
select nextval(...)
로 시퀀스 값을 미리 확보하고,
INSERT는 flush 시점에만 나간다.
즉, save() 할 때는 시퀀스만 조회하고,
실제 insert 쿼리는 트랜잭션 커밋 직전에 나감.
참고
https://vladmihalcea.com/jpa-entity-identifier-sequence/
https://thorben-janssen.com/persist-save-merge-saveorupdate-whats-difference-one-use/
'트러블슈팅' 카테고리의 다른 글
| 다국어 다루기 (0) | 2025.09.16 |
|---|---|
| Gradle에서 task.named vs tasks.withType의 차이 (0) | 2025.04.28 |
| NoOpServerSecurityContextRepository 와 ReactiveSecurityContextHolder 차이 (0) | 2025.04.12 |
| spring batch reader (0) | 2025.01.31 |
| 코틀린에서 파이썬 코드 실행하기 (2) | 2024.12.28 |