created at 2023-11-03
JPA 영속성 유지기간을 관찰하기 위한 여러 예제를 설명합니다
@Transaction
코드 밖에서는 영속성 유지가 되지 않는 것을 확인
@Override
@Transactional("REQUIRED")
@Async("taskPool")
public CompletableFuture<UserCredential> saveUser(RegisterUserRequest req) {
checkUserExistOrThrowCustomException(req.getUserId());
UserCredential user = userCredentialRepository.save(req.toEntity());
return CompletableFuture.completeFuture(user);
}
@Test
public void changeNameFunctionOutside(){
userService.saveUser(req)
.thenApply((user)->{
user.setUserName("newUserName");
return user;
}).thenAccept((user)->{
UserCredential findUser = userCredentialRepository.findById(user.getUserId()).orElseThrow(new RuntimeException());
assertThat(findUser.getUserName()).isNotEqualTo("newUserName"); // oldUserName != newUserName
})
}
@Transaction
코드 내부에서 영속성 유지가 되는 것을 확인
@Override
@Transactional("REQUIRED")
@Async("taskPool")
public CompletableFuture<UserCredential> saveUser(RegisterUserRequest req) {
checkUserExistOrThrowCustomException(req.getUserId());
UserCredential user = userCredentialRepository.save(req.toEntity());
return CompletableFuture.supplyAsync(()->{
user.setUserName("newUserName"); // 내부로 변경
return user;
});
}
@Test
public void changeNameFunctionOutside(){
userService.saveUser(req)
.thenAccept((user)->{
UserCredential findUser = userCredentialRepository.findById(user.getUserId()).orElseThrow(new RuntimeException());
assertThat(findUser.getUserName()).isEqualTo("newUserName"); // oldUserName => newUserName == newUserName
})
}
이를 통해 바뀐 생각은 아래와 같아요.
- 기존
Transaction은 ThreadLocal 하게 설정되며 @Transactional 로 설정 시, Thread 가 callback 함수를 호출하는 시점에 commit 되는 것으로 알고있었습니다. 즉, @Transactional 이 선언된 함수 내에서 thenApply 와 같은 callback 함수가 호출 되어도 엔티티는 Detached 된 상태이므로 변경되지 않을것이라고 판단했어요.
- 변경
@Transactional 로 설정 시, 함수
가 Stack 에서 빠지는 시점에 commit 이 이루어지는것을 확인하였습니다. 즉, 함수 내부에서는 ThreadLocal 하다면 어떤 것이든 트랜잭션에 담길 것이고 외부에서는 Detached 된 엔티티를 반환받는것을 확인했어요.