댓글 작성 메서드(POST) : http://localhost:8080/api/v1/posts/3/comments
{
"resultCode": "201-1",
"msg": "null번 댓글이 작성되었습니다."
}
@GeneratedValue(strategy = IDENTITY)와 같은 ID 생성 전략에서, ID는 엔티티가 DB에 실제로 저장되기 전까지 생성되지 않는다.
@Transactional 어노테이션이 붙은 메서드는 메서드는 해당 메서드가 완벽히 종료된 이후 영속성 전이가 발생한다.
즉 트랜잭션이 끝나야 DB에 insert되면서 getId()가 가능한데, 현재는 트랜잭션이 끝나지 않은 중간에 getId()를 하려고 해서 id에 null이 나오는 것이다.
@PostMapping
@Transactional
public RsData<Void> writeItem(
@PathVariable long postId,
@RequestBody @Valid PostCommentWriteReqBody reqBody
) {
Member actor = rq.checkAuthentication();
Post post = postService.findById(postId).orElseThrow(
() -> new ServiceException("404-1", "%d번 글은 존재하지 않습니다.".formatted(postId))
);
PostComment postComment = post.addComment(
actor,
reqBody.content
);
// postComment.getId()는 영속성 컨텍스트가 DB에 반영되기 전에는 null일 수 있음
return new RsData<>(
"201-1",
"%d번 댓글이 작성되었습니다.".formatted(postComment.getId())
);
}
Hibernate > 영속성 컨텍스트 저장 > 쓰기 지연으로 DB 반영 대기 > ID 생성 지연 > DB 반영 전까지 getId()는 null
public class ApiV1PostCommentController {
@Autowired //추가함
@Lazy //추가함
private ApiV1PostCommentController self;
@PostMapping
public RsData<Void> writeItem(
@PathVariable long postId,
@RequestBody @Valid PostCommentWriteReqBody reqBody
) {
PostComment postComment = self._writeItem(postId, reqBody); //추가함
return new RsData<>(
"201-1",
"%d번 댓글이 작성되었습니다.".formatted(postComment.getId())
);
}
@Transactional
public PostComment _writeItem( //추가함
long postId,
PostCommentWriteReqBody reqBody
) {
Member actor = rq.checkAuthentication();
Post post = postService.findById(postId).orElseThrow(
() -> new ServiceException("404-1", "%d번 글은 존재하지 않습니다.".formatted(postId))
);
return post.addComment(
actor,
reqBody.content
);
}
@Autowired : 현재 클래스의 프록시 객체(Proxy)를 주입받아야 하기 때문에 사용@Lazy : Spring이 실제로 self가 필요할 때만 초기화하도록 지연하기 위해 사용함.public class ApiV1PostCommentController {
private final EntityManager em; //추가함
@PostMapping
@Transactional
public RsData<Void> writeItem(
@PathVariable long postId,
@RequestBody @Valid PostCommentWriteReqBody reqBody
) {
Member actor = rq.checkAuthentication();
Post post = postService.findById(postId).orElseThrow(
() -> new ServiceException("404-1", "%d번 글은 존재하지 않습니다.".formatted(postId))
);
PostComment postComment = post.addComment(
actor,
reqBody.content
);
em.flush(); //추가함
return new RsData<>(
"201-1",
"%d번 댓글이 작성되었습니다.".formatted(postComment.getId())
);
}
Controller에서 수행되는 트랜잭션의 영속성 컨텍스트를 관리하는 객체
현재 활성화된 트랜잭션의 영속성 컨텍스트를 사용하거나, 트랜잭션이 없으면 새로운 영속성 컨텍스트를 생성
하지만 대부분의 경우 서비스 계층에서 트랜잭션이 시작되므로, Controller에서 직접 EntityManager를 사용하는 것은 권장하지 않는다.
JpaRepository의 flush() 메서드를 호출하여 변경 사항을 DB에 반영하는 방식
postService.flush();
public void flush() {
postRepository.flush(); // em.flush(); 와 동일
}
JpaRepository.flush()는 내부적으로 EntityManager.flush()를 호출하기 때문에 동작은 동일하지만, 추상화 수준에서 차이가 있다.