본문 바로가기
TIL

[231221] update 시 Timestamp 값으로 null이 반환

by 진진리 2023. 12. 21.
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 가 닫힘

출처: https://velog.io/@dnwlsrla40/JPA-Open-In-View

'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