728x90
문제 상황
게시글을 수정하는 API를 구현하는 과정에서 기존의 방식과 달리
repository.save() 매서드를 사용하여 update한 내용을 반영하고자 하였다.
이때 createdAt과 modifiedAt 필드의 값이 null로 반환되었다.
PostServiceImpl 기존 코드 :: updatePost()
@Override
@Transactional
public PostUpdateRes updatePost(Long postId, PostUpdateReq req, User user) {
Post post = postReadService.getPostEntity(postId);
PostValidator.checkPostAuthor(post, user);
Post savedPost = postRepository.save(Post.builder()
.postId(postId)
.title(req.getTitle())
.content(req.getContent())
.user(user)
.build()
);
return PostServiceMapper.INSTANCE.toPostUpdateRes(savedPost);
}
- 처음 데이터 조회했을 때 1차 캐싱
- save 할때 둘다 null 로 변경 (빌더에 createdAt, modifiedAt 값이 없으므로)
- @LastModifiedDate 는 DB에 변경사항이 일어났을 때의 시간이 저장되는 구조이므로 아직 트렌젝션이 끝나지 않은 상태에서 조회하면 캐싱된 데이터가 조회되어 둘다 null 로 노출
주의할 점:: TimeStamp의 @CreatedDate, @LastModifiedDate
@CreatedDate와 @LastModifiedDate 애너테이션은 엔티티가 저장된 시점, 수정된 시점을 필드에 기록한다.
CreatePost()와의 비교
@Override
public PostCreateRes createPost(PostCreateReq req, User user) {
Post savePost = postRepository.save(Post.builder()
.title(req.getTitle())
.content(req.getContent())
.user(user)
.build()
);
return PostServiceMapper.INSTANCE.toPostCreateRes(savePost);
}
updatePost와 코드가 거의 유사함에도 불구하고 createPost의 반환값은 잘 반환되었다.
1차 캐시로 인해 해당 문제다고 생각해서 @Transactional을 제거해보았다.
UpdatePost() 1차 수정 코드
@Override
public PostUpdateRes updatePost(Long postId, PostUpdateReq req, User user) {
Post post = postReadService.getPostEntity(postId);
PostValidator.checkPostAuthor(post, user);
Post savePost = postRepository.save(Post.builder()
.postId(postId)
.title(req.getTitle())
.content(req.getContent())
.user(user)
.build()
);
return PostServiceMapper.INSTANCE.toPostUpdateRes(savePost);
}
그러자 createdAt은 변함없이 null값이었지만 modifiedAt에는 값이 제대로 반환되었다.
- 데이터 조회, save(), 다시 조회 과정이 각각의 트랜잭션으로 분리되어 실행되었으므로 save() 단계에서 modifiedAt 값이 변경되었을 수 있음
- 하지만 트랜잭션이 끝났더라도 영속성 컨텍스트가 살아 있음
- update 쿼리가 실행되었으므로 modifiedAt 에만 데이터가 변경되고 createdAt 은 null
UpdatePost() 2차 수정 코드
이때 application.yml 파일의 jpa.open-in-view: false로 설정해준 뒤
저장한 게시글 엔티티를 다시 조회해주었더니 값이 둘 다 제대로 반환되었다.
@Override
public PostUpdateRes updatePost(Long postId, PostUpdateReq req, User user) {
Post post = postReadService.getPostEntity(postId);
PostValidator.checkPostAuthor(post, user);
postRepository.save(Post.builder()
.postId(postId)
.title(req.getTitle())
.content(req.getContent())
.user(user)
.build()
);
return PostServiceMapper.INSTANCE.toPostUpdateRes(postReadService.getPostEntity(postId));
}
- open-in-view 설정을 false로 하면서 save이후 entity manager가 닫혔기 때문에 해당 엔티티가 DB에 저장됨
- 해당 엔티티를 다시 조회함으로써 createdAt 값이 들어감
application.yml 파일의 jpa.open-in-view: false 설정
- 기본값: true true일 경우 - 트랜잭션이 종료되어도 entity mananger가 닫히지 않음
- false일 경우 - 트랜잭션을 종료할 때 entity mananger 가 닫힘
'TIL' 카테고리의 다른 글
[231228] service 테스트에서의 MultipartFile (0) | 2023.12.28 |
---|---|
[231227] 이메일 인증 구현 (링크 방식) (0) | 2023.12.27 |
[231219] QueryDSL, JPQL (0) | 2023.12.19 |
[231218] 이진트리, 힙 (0) | 2023.12.18 |
[231213] MySQL 공부 (0) | 2023.12.13 |