Creating JPA "Comment" Entity
Comment
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "comments")
public class Comment
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String body;
private String email;
private String name;
@ManyToOne(fetch =FetchType.LAZY)
@JoinColumn(name="post_id", nullable=false)
private Post post;
}
@ManyToOne(fetch =FetchType.LAZY)
@JoinColumn(name="post_id", nullable=false)
private Post post;
FetchType.LAZY
Hibernate to only Fetch the related entities from the database
when you use the relationship
@JoinColumn(name="post_id", nullable=false)
외래키 명 : post_id
실행 결과
Hibernate: create table comments (id bigint not null auto_increment, body varchar(255), email varchar(255), name varchar(255), post_id bigint not null, primary key (id)) engine=InnoDB
Hibernate: alter table comments add constraint FKh4c7lvsc298whoyd4w9ta25cr foreign key (post_id) references posts (id)
Post
@Data // getter setter requiredArgsConstructor toString
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name="posts", uniqueConstraints = {@UniqueConstraint(columnNames= {"title"})})
public class Post
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name="title", nullable=false)
private String title;
@Column(name="description", nullable=false)
private String description;
@Column(name="content", nullable=false)
private String content;
@OneToMany(mappedBy="post", cascade= CascadeType.ALL, orphanRemoval= true)
private Set<Comment> comments = new HashSet<>();
}
@OneToMany(mappedBy="post", cascade= CascadeType.ALL, orphanRemoval= true)
private Set<Comment> comments = new HashSet<>();
cascade는 Entity의 상태가 변화했을 때 관계가 있는 Entity에도 상태 변화를 전파시키는
orphanRemoval=true는 관계가 끊어진 child를 자동으로 제거
참고 : Cascade 이해 및 orphanRemoval=true vs CascadeType.REMOVE
Creating JPA Repository
public interface CommentRepository extends JpaRepository<Comment, Long>
{
}
※ SimpleJpaRepository 에 @Repository 애노테이션이 선언돼있기 때문에 CommentRepository 위에 애노테이션을 선언할 필요가 없다.
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<t, id=""> implements JpaRepositoryImplementation<t, id=""> { .. }</t,></t,>
Creating Comment DTO
Create CommentDTO class to send the data between client and server.
@Data
public class PostDto
{
private long id;
private String title;
private String description;
private String content;
}
CRUD
Create Comment
Service
public interface CommentService
{
CommentDto createComment(long postId, CommentDto commentDto);
}
ServiceImpl
@Service
public class CommentServiceImpl implements CommentService
{
private CommentRepository commentRepository;
private PostRepository postRepository;
@Autowired
public CommentServiceImpl(CommentRepository commentRepository, PostRepository postRepository) {
super();
this.commentRepository = commentRepository;
this.postRepository = postRepository;
}
@Override
public CommentDto createComment(long postId, CommentDto commentDto) {
// retrieve post entity by id
Post post = postRepository.findById(postId).orElseThrow(() -> new ResourceNotFoundException("Post", "id", postId));
// set Post to comment entity
Comment comment = mapToEntity(commentDto);
comment.setPost(post);
// comment entity to DB
Comment newComment = commentRepository.save(comment);
return mapToDTO(newComment);
}
private CommentDto mapToDTO(Comment comment)
{
CommentDto commentDto = new CommentDto();
commentDto.setId(comment.getId());
commentDto.setName(comment.getName());
commentDto.setEmail(comment.getEmail());
commentDto.setBody(comment.getBody());
return commentDto;
}
private Comment mapToEntity(CommentDto commentDto)
{
Comment comment = new Comment();
comment.setId(commentDto.getId());
comment.setName(commentDto.getName());
comment.setEmail(commentDto.getEmail());
comment.setBody(commentDto.getBody());
return comment;
}
}
mapToDTO, mapToEntity 추가
Controller
@RestController
@RequestMapping("/api")
public class CommentController
{
private CommentService commentService;
public CommentController(CommentService commentService) {
super();
this.commentService = commentService;
}
@PostMapping("/posts/{postId}/comments")
public ResponseEntity<CommentDto> createComment(
@PathVariable long postId,
@RequestBody CommentDto commentDto){
return new ResponseEntity<>(commentService.createComment(postId, commentDto), HttpStatus.CREATED);
}
}
TEST
Gat All Comments By Post Id
Service
List<CommentDto> getCommentsByPostId(long postId);
ServiceImpl
@Override
public List<CommentDto> getCommentsByPostId(long postId)
{
List<Comment> comments = commentRepository.findByPostId(postId);
return comments.stream().map(comment -> mapToDTO(comment)).collect(Collectors.toList());
}
CommentRepository
public interface CommentRepository extends JpaRepository<Comment, Long>
{
List<Comment> findByPostId(long postId);
}
Controller
// get all comments by postid
@GetMapping("/posts/{postId}/comments")
public List<CommentDto> getCommentsByPostId(@PathVariable Long postId){
return commentService.getCommentsByPostId(postId);
}
TEST
http://localhost:8080/api/posts/2/comments
Post id 가 2인 모든 Comment
Get Comment By Id
Service
CommentDto getCommentById(Long postId, Long commentId);
ServiceImpl
@Override
public CommentDto getCommentById(Long postId, Long commentId) {
Post post = postRepository.findById(postId).orElseThrow(
()-> new ResourceNotFoundException("Post", "id", postId));
Comment comment =commentRepository.findById(commentId).orElseThrow(
()-> new ResourceNotFoundException("Comment", "id", commentId));
if(!post.getId().equals(comment.getPost().getId())) {
throw new BlogAPIException(HttpStatus.BAD_REQUEST , "Comment does not belong to post");
}
return mapToDTO(comment);
}
Exception 추가
비지니스 로직이나 유효성 검사시 예외 처리
//whenever we write some business logic or validate request parameters
public class BlogAPIException extends RuntimeException
{
private HttpStatus status;
private String message;
public BlogAPIException(String message, HttpStatus status, String message2) {
super(message);
this.status = status;
message = message2;
}
public BlogAPIException(HttpStatus status, String message) {
super();
this.status = status;
this.message = message;
}
public HttpStatus getStatus() {
return status;
}
@Override
public String getMessage() {
return message;
}
}
Controller
@GetMapping("/posts/{postId}/comments/{commentId}")
public ResponseEntity<CommentDto> getCommentById(@PathVariable Long postId, @PathVariable Long commentId){
return new ResponseEntity<>(commentService.getCommentById(postId, commentId), HttpStatus.OK);
}
TEST
Comment 값은 있는데
게시물에 해당 Comment가 없을 경우
http://localhost:8080/api/posts/1/comments/1
사용자 정의 예외 (BlogAPIException) 발생
Comment 값도 없고
게시물에 해당 Comment가 없을 경우
사용자 정의 예외 (ResourceNotFoundException) 발생
Update Comment By Id
수정하기
CommentService
CommentDto updateComment(Long postId, long commentId, CommentDto commentRequest);
CommentServiceImpl
@Override
@Transactional
public CommentDto updateComment(Long postId, long commentId, CommentDto commentRequest) {
Post post = postRepository.findById(postId).orElseThrow(
()-> new ResourceNotFoundException("Post", "id", postId));
Comment comment =commentRepository.findById(commentId).orElseThrow(
()-> new ResourceNotFoundException("Comment", "id", commentId));
if(!post.getId().equals(comment.getPost().getId())){
throw new BlogAPIException(HttpStatus.BAD_REQUEST,"Comment does not belongs to post");
}
comment.setName(commentRequest.getName());
comment.setEmail(commentRequest.getEmail());
comment.setBody(commentRequest.getBody());
return mapToDTO(comment);
}
CommentController
@PutMapping("/posts/{postId}/comments/{id}")
public ResponseEntity<CommentDto> updateComment(@PathVariable Long postId, @PathVariable(value="id") Long commentId,
@RequestBody CommentDto commentDto)
{
return new ResponseEntity<>(commentService.updateComment(postId, commentId, commentDto), HttpStatus.OK);
}
TEST
STS 콘솔창에서 update 쿼리가 나가는 것을 확인
Hibernate: select comment0_.id as id1_0_0_, comment0_.body as body2_0_0_, comment0_.email as email3_0_0_, comment0_.name as name4_0_0_, comment0_.post_id as post_id5_0_0_ from comments comment0_ where comment0_.id=?
Hibernate: update comments set body=?, email=?, name=?, post_id=? where id=?
Delete Comment
Service
void deleteComment(Long postId, long CommentId);
ServiceImpl
@Override
public void deleteComment(Long postId, long commentId) {
Post post = postRepository.findById(postId).orElseThrow(
()-> new ResourceNotFoundException("Post", "id",postId ));
Comment comment =commentRepository.findById(commentId).orElseThrow(
()-> new ResourceNotFoundException("Comment", "id", commentId));
if(!post.getId().equals(comment.getPost().getId())) {
throw new BlogAPIException(HttpStatus.BAD_REQUEST, "Comment does not belongs to post");
}
commentRepository.delete(comment);
}
Controller
@DeleteMapping("/posts/{postId}/comments/{id}")
public ResponseEntity<String> deleteComment(@PathVariable Long postId, @PathVariable(value="id") Long commentId)
{
commentService.deleteComment(postId, commentId);
return new ResponseEntity<String>("Comment deleted Successfully", HttpStatus.OK);
}
TEST
Hibernate: delete from comments where id=?
'Back-end > Spring Boot + REST API' 카테고리의 다른 글
Spring boot - blog application (REST API) : Global Exception Handling (0) | 2022.07.05 |
---|---|
Spring boot - blog application (REST API) : ModelMapper (0) | 2022.07.04 |
Spring boot - blog application (REST API) : Pagination and Sorting Support (0) | 2022.06.30 |
Spring boot - blog application (REST API) : Post CRUD (0) | 2022.06.29 |
Spring boot - blog application (REST API) (0) | 2022.06.28 |
댓글