[문제 상황]
//service
@Transactional
public void createComment(AuthUser authUser, CommentRequest commentRequest){
commentRepository.save(
Comment.builder()
.post(postRepository.findByIdAndStatus(commentRequest.getPostId(), ACTIVE)
.orElseThrow(PostNotFoundException::new))
.parent(commentRequest.getParentId() != null ?
getParent(commentRequest.getParentId()) : null)
.content(commentRequest.getContent())
.user(authUser.getUser())
.build());
authUser.getUser().addScore(1);
}
---
(User 엔티티 내부 메서드)
public void addScore(int num){
this.score+=num;
}
댓글을 단 유저에게 점수 1점을 부여하는 로직이다. 여느 때와 다름없이 변경감지를 통해 실행되는줄 알았으나 DB를 보니 점수가 올라가지 않은 문제를 발견했다.
[원인]
//controller
@PostMapping("")
public void createComment(@AuthenticationPrincipal AuthUser authUser, @RequestBody @Valid CommentRequest commentRequest){
commentService.createComment(authUser,commentRequest);
}
@AuthenticationPrincipal을 통해 로그인한 유저 정보를 받아온 뒤 다른 객체와의 유효한 유저 검증 또는 유저 데이터를 불러오는 용도로 주로 사용했었다.
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) {
User user = userRepository.findByEmailAndStatus(email, BaseEntity.Status.ACTIVE)
.orElseThrow(UserNotFoundException::new);
return AuthUser.of(user);
}
}
public class AuthUser implements UserDetails {
private User user;
public AuthUser(User user) {
this.user = user;
}
public static AuthUser of(User user) {
return new AuthUser(user);
}
public Long getUserId() {
return user.getId();
}
public Provider getProvider() {
return user.getProvider();
}
public User getUser() {
return user;
}
...
}
loadUserByUsername 메서드를 통해 DB에서 User를 조회해 온 뒤 UserDetails을 implements한 AuthUser에 user 객체를 넣어주기 때문에 영속성 컨텍스트에 당연히 user객체가 있을줄 알았다.
하지만 데이터베이스 커넥션 리소스를 낭비하지 않기위해
OSIV OFF한 상태이다. 그래서 User 객체의 영속성 컨텍스트 생존 범위가 Service단에서만 살아있다.
OSIV ON 일 때
OSIV OFF 일 때
[해결방안]
@Transactional
public void createComment(AuthUser authUser, CommentRequest commentRequest){
User user = userRepository.findByEmailAndStatus(authUser.getUsername(), BaseEntity.Status.ACTIVE)
.orElseThrow(UserNotFoundException::new);
commentRepository.save(
Comment.builder()
.post(postRepository.findByIdAndStatus(commentRequest.getPostId(), ACTIVE)
.orElseThrow(PostNotFoundException::new))
.parent(commentRequest.getParentId() != null ?
getParent(commentRequest.getParentId()) : null)
.content(commentRequest.getContent())
.user(user)
.build()
);
user.addScore(1);
}
service 단에서 authUser에 등록된 유저 정보로 DB에서 엔티티를 조회하여 영속성 컨텍스트에서 관리할 수 있게 한 뒤 변경 감지를 실행했다.