엔티티: 셀프참조로 부모 댓글과 자식 댓글 리스트 참조
댓글저장: parentId 값을 통해 부모를 지정
댓글조회: 부모가 없는 댓글 먼저, 그 후에 생성날짜 내림차순으로 정렬
댓글조회+: 정렬 후 ResponseDto 객체에 JSON 형식으로 댓글 구조 생성 후 응답
댓글삭제: 하위 댓글이 삭제되었으면 삭제, 남아있다면 상태만 삭제로 변경
먼저 생각해보면 좋은 포인트
public class Comment {
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent")
private Comment parent; // 부모 댓글
@OneToMany(fetch = FetchType.LAZY, mappedBy = "parent", orphanRemoval = true)
private List<Comment> children = new ArrayList<>(); // 자식 댓글
}
하나의 엔티티 안에 자신을 필드 변수로 가지고 있는 것이 셀프 참조이다
댓글 엔티티 안에 parent라는 댓글과, children이라는 댓글 리스트를 가지고 있다
<연관관계>
OneToMany
parent <-----------------> children
"부모 댓글(One) To (Many) 자식댓글"의 구조이며 mappedBy속성으로 부모 댓글을 주인으로 지정하였으며, 부모 댓글이 삭제될 때 자식 댓글도 같이 삭제될 수 있게 고아 객체 삭제 옵션(orphanRemoval = true)도 넣었다.
여기서 children의 mappedBy = "parent"는 다대일 양방향 관계에서 연관관계의 주인을 설정하는 방법이다. mappedBy에서 왜 -ed가 붙은 수동태인지를 생각해보면 이해가 쉬운데
mappedBy = "parent"
: 나는 관계를 "맺음 당하는" 쪽이며 주인님은 나를 "parent"라고 지정하였다
여기서 중요한 점은 "나"와 "주인님"은 누구인가?이다.
🏴 "나" : 지금 댓글 Entity이다.
🚩 "주인님" : 지금 댓글 Entity 안의 Children이다.
mappedBy의 값으로 "parent"가 지정되어있다고 댓글 엔티티 안의 parent일 거라고 생각하면 안된다. mappedBy = "parent"는 연관관계의 주인을 자식댓글로 지정하며 자식 댓글은 부모 댓글을 "parent"라는 필드명으로 참조한다는 설정이다.
public class CommentService {
...
public Comment createComment(Comment comment, Long parentId) {
// 부모가 있는 대댓글이라면
if(parentId != null) {
// 부모댓글의 유효성 검증
Comment parent = findVerifiedComment(parentId);
// 부모 댓글 설정
comment.updateParent(parent);
}
return commentRepository.save(comment);
}
}
댓글 저장은 부모가 있는 경우와 없는 경우로 나뉜다.
1) 부모가 있는 경우
부모 댓글의 id값을 받아서 조회하고, 연관관계의 주인인 자식 댓글에서 부모를 지정하고 save 메서드를 실행한다. 연관관계의 주인인 자식 댓글에서 부모 댓글을 지정하는 것으로 데이터베이스는 OK이지만, 객체로 따졌을 때는 부모 댓글에도 자식 댓글을 넣어주는 것이 좋다.
public class Comment {
...
public void updateParent(Comment parent){
this.parent = parent;
parent.getChildren().add(this);
}
}
자식댓글에서만 부모 댓글을 지정했다면, 데이터베이스에 저장이 되기 전에는 부모 댓글은 자식 댓글의 데이터를 참조하고 있지 않다. JPA에서는 영속성 컨텍스트가 존재해서 캐시 기능을 하기 때문에, 데이터 동기화를 위해서 부모댓글에도 자식을 넣고, 자식 댓글에도 부모를 지정하는 것이 좋다.
2) 부모가 없는 경우
그냥 저장한다.
안녕하세요 글 잘 봤습니다.
findVerifiedComment는 어떻게 구현했는지 알 수 있을까요?