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의 중요 개념이기 때문에 따로 공부할 필요가 있습니다! (CommentRepositorypublic 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
This series has been super helpful for understanding how to build a comment system in Spring—clear and practical! Just like a well-built board, a good http://thevapez.co.uk should be smooth and reliable. Thanks for sharing!
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.