728x90
서론
프로젝트를 진행하면서, 하나의 Update API를 개발하고 있었다.
요구사항에서는 update 후 해당 데이터의 정보를 Return 하도록 구성되어 있었다.
업데이트 처리 후 해당 객체를 바로 Return 하도록 진행하였는데, lastModifiedAt의 경우에는 update 쿼리가 날아간 시간이 잘 적혀있는 반면 createdAt은 Null로 반환되었다.
바로 DB를 실행시켜 해당 데이터를 확인했을 땐, createAt과 lastModifiedAt이 모두 들어있는 상황이었다.
Comment Entity
...
public class Comment extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String comment;
@JoinColumn(name = "user_id")
@ManyToOne(fetch = LAZY)
private User user;
@JoinColumn(name = "post_id")
@ManyToOne(fetch = LAZY)
private Post post;
private LocalDateTime deletedAt;
public static Comment toEntity(User user, Post post, CommentRequest request) {
return Comment.builder()
.user(user)
.post(post)
.comment(request.getComment())
.build();
}
public CommentReadResponse toResponse() {
return CommentReadResponse.builder()
.id(this.id)
.comment(this.comment)
.userName(this.user.getUserName())
.postId(this.post.getId())
.createdAt(this.getCreatedAt())
.build();
}
}
Base Entity
...
public abstract class BaseEntity {
/* 사용할 일이 없으므로 설계상 추상클래스로 만드는 것이 실수를 방지할 수 있다 */
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime lastModifiedAt;
private LocalDateTime deletedAt;
}
본론
해당 문제를 해결하기 위해 먼저 어느 위치에서 createdAt 변수의 값이 null이 되는지를 확인해야 했다.
@Service
@RequiredArgsConstructor
public class CommentService {
private final CommentRepository commentRepository;
private final AlarmRepository alarmRepository;
private final ValidateService service;
@Transactional
public CommentModifyResponse modifyComment(Integer postId, Integer id, CommentRequest request, String userName) {
User user = service.validateGetUserByUserName(userName);
Post post = service.validateGetPostById(postId);
Comment comment = service.validateGetCommentById(id);
service.validateMatchUsers(user, comment);
Comment savedComment = Comment.builder()
.id(comment.getId())
.user(user)
.post(post)
.comment(request.getComment())
.build();
return CommentModifyResponse.of(commentRepository.save(savedComment));
}
}
해당 메서드로 진행해 본 결과 정확히 빌더로 새로운 객체를 만들 때부터 createdAt은 null이 되기 시작했다.
DB에는 createdAt의 값이 무사히 들어있고 나머지 값들만 update 된 것을 확인하고 다른 방법을 한 가지 생각했다.
그렇다면, 저장 후에 다시 쿼리를 날려서 정보를 받아오자.
savedComment로 save를 한 뒤 find 메서드를 사용하여 정보를 다시 받아왔으나 역시 null로 반환되었다.
일단 createdAt이 null이 되는 이유.
- 우리는 Base Entity를 먼저 확인해야 한다. Base Entity의 createdAt부분을 확인해 보면 @Column(updatable = false)가 되어있는 것을 볼 수 있다. update 쿼리가 날아가게 되면 해당 칼럼은 update가 되지 않는 것이다.
- 동일한 pk 값을 갖는 엔티티 데이터가 save 메서드를 날릴 경우에 JPA는 insert 쿼리가 아닌 update 쿼리를 날리게 된다.
- 즉 savedComment 객체에 담긴 내용은 update 쿼리를 날리게 되고 updatable이 false인 createdAt은 update가 되지 않는다.
- builder를 사용할 때, 날짜 값을 이어받지 않았기 때문에 createdAt과 lastModifiedAt은 모두 null로 되어있다.
- 하지만 createdAt은 updateable이 false로 되어있으므로 update시에 이전 시간도, 현재 시간도 아닌 null이 담기는 것.
savedComment에 null이 담겨 있는 것은 당연한 이유였다.
select를 해도 null인 이유?
- save를 하면서 createdAt이 null인 savedComment의 정보가 영속성 콘텍스트에 담겨 있기 때문에, select로 정보를 불러와도 null인 정보를 불러오게 된다.
이를 해결하기 위해 flush를 한 후 find를 시도해보았지만 실패했다.
결론
Comment Entity에 set을 하기 위한 Entity를 하나 만들어서 기존 정보를 받아온 객체에 comment만 수정하여 save(update 쿼리)하는 방법으로 로직을 변경하였다.
...
public class Comment extends BaseEntity{
...
public void update(String comment) {
this.comment = comment;
}
}
반응형
'Server > Spring&Spring Boot' 카테고리의 다른 글
[Spring Boot] SpringBoot 3.0.x 이상에서 Swagger 사용 (0) | 2023.01.19 |
---|---|
[Spring Boot] Spring Security 6.0 Configuration. (0) | 2023.01.17 |
[Spring] 전역 예외, Global Exception 생성 (0) | 2022.12.21 |
[Spring] Custom Response 생성 (0) | 2022.12.21 |
[Spring Security] config 설정 2 (0) | 2022.12.04 |