JPA find 후 엔티티 변경시 UPDATE가 안날라갈때 with Transaction
앱과 통신을 위한
REST API 서버를 만드는중이다.!
UPDATE를 수행하는 메서드를 만들었다. 다음과 같다.
@Service
public class UserService {
...
public User updateUser(Long id, User userInfo) {
User user = userRepository.findById(id).get(); // Optional 수정 필요
String name = userInfo.getName();
int age = userInfo.getAge();
user.setName(name);
user.setAge(age);
return user;
}
}
아니!! find는 쿼리가 잘 날라가는데
아래의 엔티티 변경시 UPDATE 쿼리가 안날라간다.
문제는 트랜잭션내에서 동작하지 않아서이다.
@Service
@Transactional
public class UserService {
...
public User updateUser(Long id, User userInfo) {
User user = userRepository.findById(id).get(); // Optional 수정 필요
String name = userInfo.getName();
int age = userInfo.getAge();
user.setName(name);
user.setAge(age);
return user;
}
}
메서드단위로 @Transactional을 붙일 수 있지만
Service 클래스에 붙였다.
궁금증 두가지가 생겼다. 하나하나 이해해보자.
1. 왜 UPDATE는 트랜잭션 없이 돌아가지 않고. INSERT, SELECT, DELETE 는 잘 돌아갔는가?
다른 쿼리(INSERT, SELECT, DELETE)가 운 좋게 하나의 트랜잭션에서 수행이 가능해서 그런것 같다.
즉, 트랜잭션의 특성을 잘 갖는다.
UPDATE의 경우 여러 쿼리가 필요하기에 각기 다른 트랜잭션에서 수행했기에 예기치 못한 상황이 발생한 것 같다.
쨋든 DB를 사용할때 트랜잭션을 사용하는건 당연한것!
트랜잭션이 ACID 특성을 갖는것을 기억하자.
2. 왜 find를 수행하고 변경 사항을 save 해주지 않아도 되는가?
옛날에 블로그에 공부하여 정리했지만 까먹었었다 ㅎㅎ.
https://non-stop.tistory.com/482 를 참고하자.
핵심은 다음과 같다.
변경 감지(dirty checking)
em.find()하여 수정한 후 em.update()를 해야할 것 같지만 아니다.
1. JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 저장(스냅샷)해둔다.
2. 플러시 시점에 스냅샷과 엔티티를 비교하여 변경된 엔티티를 찾아 쓰기 지연 SQL 저장
3. 쓰기 지연 저장소 SQL을 DB에 반영
4. 트랜잭션을 커밋
=> 변경 감지는 영속 상태의 엔티티에만 적용된다.
쓰기 지연 SQL ?
em.persist(member)를 한다고 바로 INSERT SQL을 DB에 보내지 않는다.
트랜잭션 커밋 직전까지 1차 캐시에 반영(저장)하며 내부 쿼리 저장소에 SQL을 차곡차곡 모았다가
트랜잭션 커밋할 때 flush하여 모아둔 쿼리를 DB에 보낸다.
flush -> commit 순서!