seong
JPA - 댓글 작성 및 삭제 기능 본문
만들기전 필요 부분 생각
- Comment 테이블 설계
하나의 게시글에 여러가지 댓글 Board와 Comment는 1:n 관계이므로 Comment에 FK
한명의 유저는 여러개의 댓글 작성 가능 User와 Comment는 1:n 관계 이므로 Comment에 FK
= 그럼 총 FK는 2개가 된다.
- 댓글은 게시글 상세보기에 포함된다.
BoardDetailRespDto에 댓글 Dto 추가 필요함.
작성
1. Comment.java
@ManyToOne - FK
하나의 게시글엔 여러개의 댓글, 여러명의 유저가 쓸 수 있다.
테이블 설계 할때 1:n의 관계가 나오면 항상 n 쪽에 FK가 걸린다.
@Getter
@NoArgsConstructor
@Entity
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content;
// Many(Comment) to One(User) - 한명의 유저가 여러개의 댓글 작성
@ManyToOne(fetch = FetchType.LAZY)
private User user;
// Many(Comment) to One(Board) - 하나의 댓글에 여러개의 댓글 작성
@ManyToOne(fetch = FetchType.LAZY)
private Board board;
}
2. Board.java
댓글은 보통 게시글을 볼때 같이 보인다. -> Board에 추가.
하나의 게시글에 여러개의 댓글이 있다.
그래서 List가 필요하다. 하지만 단순 List로 주면 테이블이 만들어질 때 List라는 타입은 없어서 에러가발생한다.
mappedBy = "테이블 변수명"을 하면 테이블 만들때 컬럼이 자동으로 생성 되지 않고
조회전용으로 만들어준다.
3. CommentRepository.java
package site.metacoding.white.domain;
import java.util.Optional;
import javax.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j // 로그확인을 위한 어노테이션
@RequiredArgsConstructor
@Repository
public class CommentRepository {
// 댓글 등록, 댓글 삭제, 댓글 수정 (추후 구현)
private final EntityManager em;
public Comment save(Comment comment) {
em.persist(comment);
return comment;
}
public void deleteById(Long id) {
em.createQuery("delete from Comment c where c.id = :id")
.setParameter("id", id)
.executeUpdate();
}
// Optinal - 예외처리
public Optional<Comment> findById(Long id) {
try {
Optional<Comment> commentOP = Optional
.of(em.createQuery("select b from Comment c where c.id = :id", Comment.class)
.setParameter("id", id)
.getSingleResult());
return commentOP;
} catch (Exception e) {
return Optional.empty();
}
}
}
4. CommentService.java
@Slf4j
@RequiredArgsConstructor
@Service
public class CommentService {
private final CommentRepository commentRepository;
private final BoardRepository boardRepository;
@Transactional
public CommentSaveRespDto save(CommentSaveReqDto commentSaveReqDto) {
// 1. Board가 있는지 확인
Optional<Board> boardOP = boardRepository.findById(commentSaveReqDto.getBoardId());
if (boardOP.isPresent()) {
// 2. Commtent객체 만들기
Comment comment = commentSaveReqDto.toEntity(boardOP.get());
Comment commentPS = commentRepository.save(comment);
CommentSaveRespDto commentSaveRespDto = new CommentSaveRespDto(commentPS);
return commentSaveRespDto;
} else {
throw new RuntimeException("댓글 작성할 게시글이 없음.");
}
}
@Transactional
public void deleteById(Long id) {
Optional<Comment> commentOP = commentRepository.findById(id);
if (commentOP.isPresent()) {
commentRepository.deleteById(id);
} else {
throw new RuntimeException("해당" + id + "로 삭제할 수 없습니다.");
}
}
}
5. CommentController.java
@RequiredArgsConstructor
@RestController
public class CommentApiController {
private final CommentService commentService;
private final HttpSession session;
@PostMapping("/comment")
public ResponseDto<?> save(@RequestBody CommentSaveReqDto commentSaveReqDto) {
SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser");
if (sessionUser == null) {
throw new RuntimeException("로그인을 진행 해주세요");
}
commentSaveReqDto.setSessionUser(sessionUser);
return new ResponseDto<>(1, "성공", commentService.save(commentSaveReqDto));
}
@DeleteMapping("comment/{id}")
public ResponseDto<?> deleteById(@PathVariable Long id) {
SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser");
if (sessionUser == null) {
throw new RuntimeException("로그인을 진행 해주세요");
}
commentService.deleteById(id);
return new ResponseDto<>(1, "성공", null);
}
}
6. RequestDto, ResponseDto를 만들어야한다..
- CommentReqDto
public class CommentReqDto {
@Getter
@Setter
public static class CommentSaveReqDto {
private String content;
private SessionUser sessionUser;// 서비스 로직
private Long boardId;
//요청의 Dto는 메서드로 처리
public Comment toEntity(Board board) { // 현재 Comment의 ReqDto에는 BoardId만 있다. 그래서 Board객체가 필요할 경우 매개변수로 작성
Comment comment = Comment.builder()
.content(content)
.board(board)// 어떤 게시글
.user(sessionUser.toEntity())
.build();
return comment;
}
}
}
- CommentResponseDto
public class CommentRespDto {
@Getter
@Setter
public static class CommentSaveRespDto {
private Long id;
private String content;
private UserDto user;
private BoardDto board;
@Getter
@Setter
public static class UserDto {
private Long id;
private String username;
public UserDto(User user) {// LAZY 로딩 시점
this.id = user.getId();
this.username = user.getUsername();
}
}
@Getter
@Setter
public static class BoardDto {
private Long id;
public BoardDto(Board board) {// LAZY 로딩 시점
this.id = board.getId();
}
}
//응답의 Dto는 생성자로 처리
public CommentSaveRespDto(Comment comment) {
this.id = comment.getId();
this.content = comment.getContent();
this.board = new BoardDto(comment.getBoard());
this.user = new UserDto(comment.getUser());
}
}
}
7. BoardDetailRespDto 에 CommentDto추가
List<Comment>를 Dto로 변환할때 두가지 방법이 있다.
1. for문으로 모두 변환
//for문 사용 방법
for (Comment c : board.getComments()) {
this.comments.add(new CommentDto(c));
}
2. stream방식
board.getComments() - board객체에 Comments를 가져옴
.stream() - 객체의 컬렉션을 없애둠, (여기선 List이므로 List<CommentDto> -> CommentDto로 변환됨.)
.map((comment) -> new CommentDto(comment)) - comment를 CommentDto로 옮긴다.
.collect(Collectors.toList()); - 다시 List로 만들어준다.
this.comment = board.getComments().stream().map((comment) -> new CommentDto(comment))
.collect(Collectors.toList());
BoardDetailRespDto.java
@Setter
@Getter
public static class BoardDetailRespDto {
private Long id;
private String title;
private String content;
private BoardUserDto user;
private List<CommentDto> comments = new ArrayList<>();//Comment데이터
@Setter
@Getter
public static class BoardUserDto {
private Long id;
private String username;
public BoardUserDto(User user) {
this.id = user.getId();
this.username = user.getUsername();
}
}
@Setter
@Getter
public static class CommentDto {
private Long id;
private String content;
private CommentUserDto user;
@Setter
@Getter
public static class CommentUserDto {
private Long id;
private String username;
public CommentUserDto(User user) {
this.id = user.getId();
this.username = user.getUsername();
}
}
public CommentDto(Comment comment) {
this.id = comment.getId();
this.content = comment.getContent();
this.user = new CommentUserDto(comment.getUser());
}
}
public BoardDetailRespDto(Board board) {
this.id = board.getId();
this.title = board.getTitle();
this.content = board.getContent();
this.user = new BoardUserDto(board.getUser());
// List<CommentDto> 여기에 List<Comment>를 옮긴다.
// 방법1
// this.comment = board.getComments().stream().map((comment) -> new
// CommentDto(comment))
// .collect(Collectors.toList());
// 방법2
for (Comment c : board.getComments()) {
this.comments.add(new CommentDto(c));
}
}
}
'JPA' 카테고리의 다른 글
JPQL의 Join문법 - join fetch (0) | 2022.10.27 |
---|---|
JPA DTO로 변환 하기 - 예제 Board Save (0) | 2022.10.26 |
JPA DTO로 변환 하기 - 예제 findAll (0) | 2022.10.26 |
Optional 사용해서 예외처리 하기 - findById예시 (0) | 2022.10.26 |
Dto로 만드는 이유, UserJoin 실습 (0) | 2022.10.26 |