Comment
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Comment extends TimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "comment_id")
private Long comment_id;
@Column(name = "comment_content")
private String content;
@Column(name = "comment_writer")
private String writer;
@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "board_id")
private Board board;
@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
User
의 comments
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
//...
@JsonManagedReference
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
//...
}
Board
의 comments
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Board extends TimeEntity {
//...
@JsonManagedReference
@OneToMany(mappedBy = "board", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments;
//...
}
fetch
, mappedBy
, cascade
, orphanRemoval
은 JPA의 중요 개념이기 때문에 따로 공부할 필요가 있습니다! (CommentRepository
public interface CommentRepository extends JpaRepository<Comment, Long> {
List<Comment> findByBoard(Board board);
Optional<Comment> findByUserAndBoard(User user, Board board);
}
List<Comment> findByBoard(Board board);
: 특정 게시판의 모든 댓글 조회Optional<Comment> findByUserAndBoard(User user, Board board);
: 특정 게시판의 특정 사용자가 작성한 단일 댓글 조회CommentFindService
@Service
@RequiredArgsConstructor
public class CommentFindService {
private final CommentRepository commentRepository;
private final BoardFindService boardFindService;
private final UserFindService userFindService;
@Transactional(readOnly = true)
public List<Comment> findAllCommentsInBoard(Long boardId) {
Board board = boardFindService.findById(boardId);
return commentRepository.findByBoard(board);
}
@Transactional(readOnly = true)
public Comment findCommentByUserAndBoard(Long userId, Long boardId) {
User user = userFindService.findById(userId);
Board board = boardFindService.findById(boardId);
return commentRepository.findByUserAndBoard(user, board)
.orElseThrow(() -> new NotFoundCommentException(String.format("There is no comment")));
}
}
BoardFindService
와 UserFindService
를 주입시켰습니다.CommentWriteService
FirebaseCloudMessageService
는 FCM을 통한 댓글 알림 서비스 구현 부입니다. 이 후 포스팅 할 글에 해당합니다.
@Slf4j
@Service
@RequiredArgsConstructor
public class CommentWriteService {
private final CommentRepository commentRepository;
private final UserFindService userFindService;
private final BoardFindService boardFindService;
private final FirebaseCloudMessageService messageService;
@Transactional
public Long writeComment(Long userId, Long boardId, CommentWriteRequest commentWriteRequest) throws FirebaseMessagingException, IOException, ExecutionException, InterruptedException {
Board board = boardFindService.findById(boardId);
User user = userFindService.findById(userId);
Comment comment = Comment.builder() // 중요 부분
.content(commentWriteRequest.getContent())
.writer(user.getName())
.board(board)
.build();
Comment savedComment = commentRepository.save(comment);
user.writeComment(savedComment);
String targetToken = board.getUser().getDeviceToken();
sendMessageToBoardWriter(targetToken, "Comment Notification!", comment.getWriter(), comment.getContent());
return savedComment.getComment_id();
}
private void sendMessageToBoardWriter(String targetToken, String title, String writer, String content) throws FirebaseMessagingException, IOException, ExecutionException, InterruptedException {
messageService.sendMessageTo(targetToken, title, "[" + writer + "]" + "가 댓글 : <" + content + ">을 작성했습니다.");
}
}
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Comment extends TimeEntity {
//...
@Builder
public Comment(String content, String writer, Board board) {
this.content = content;
this.writer = writer;
writtenBoard(board);
}
private void writtenBoard(Board board) {
this.board = board;
board.getComments().add(this);
}
//...
}
@Builder
를 통해 객체를 생성할 때 request
를 통해 넘어온 댓글의 내용과 UserFindService
를 통해 찾은 사용자의 정보를 사용했습니다.writtenBoard()
)를 구현했습니다.user.writeComment(savedComment)
)User
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
//...
public void writeComment(Comment comment) {
this.comments.add(comment);
comment.writeUser(this);
}
//...
}
Comment
//...
public void writeUser(User user) {
this.user = user;
}
//...
}
CommentUpdateService
@Service
@RequiredArgsConstructor
public class CommentUpdateService {
private final CommentFindService commentFindService;
@Transactional
public Long updateComment(Long userId, Long boardId, CommentUpdateRequest commentUpdateRequest) {
Comment comment = commentFindService.findCommentByUserAndBoard(userId, boardId);
return comment.update(
commentUpdateRequest.getContent()
);
}
}
CommentDeleteService
@Service
@RequiredArgsConstructor
public class CommentDeleteService {
private final CommentFindService commentFindService;
private final CommentRepository commentRepository;
@Transactional
public void deleteComment(Long userId, Long boardId) {
Comment comment = commentFindService.findCommentByUserAndBoard(userId, boardId);
commentRepository.deleteById(comment.getComment_id());
}
}
참고 : 댓글이 삭제될 경우, 게시판과 사용자 측면에서 조회할 수 없게 되지만, 사용자나 게시판이 삭제될 경우 그에 해당하는 댓글은 저절로 삭제가 됩니다. ⇒ 연관관계를 맺을 때 옵션을 사용했기 때문입니다.
API 구현 부는 앞선
User
,Board
와 매우 유사한 구조이기 때문에 생략하겠습니다.
특히, 필요한 정보는@PathVariable
및@RequestBody
+XXXRequest
클래스의 파라미터를 통해 얻을 수 있도록 구현했습니다.
This is a solid implementation for a commenting system where users can add, update, and delete comments on boards. The use of JPA relationships PolyTrack and cascading operations simplifies the management of associated entities.
Visiting the city and feeling like you’d enjoy some pleasant and friendly company? Call Girls in Crowne Plaza Hotel seems to be what you were searching for. While I do not offer such services in my scope, I will be able to offer an engaging conversation and an enjoyable experience.
The nightlife around Fabhotel Prime Sage is amazing, especially with the availability of Call Girls in Fabhotel Prime Sage. It makes for an exciting night with great company
Enter the pixelated world of Retro Bowl College where football fans can enjoy a delightful blend of strategy and action. Manage your own team, make tactical decisions, and lead them to victory on the field. With its charming visuals and engaging gameplay, Retro Bowl is an ideal choice for casual players and sports enthusiasts alike.