diff --git a/src/main/java/com/example/jpa/comment/controller/CommentController.java b/src/main/java/com/example/jpa/comment/controller/CommentController.java new file mode 100644 index 0000000..06268aa --- /dev/null +++ b/src/main/java/com/example/jpa/comment/controller/CommentController.java @@ -0,0 +1,59 @@ +package com.example.jpa.comment.controller; + +import com.example.jpa.comment.dto.request.CommentCreateRequest; +import com.example.jpa.comment.dto.request.CommentUpdateRequest; +import com.example.jpa.comment.dto.response.CommentResponse; +import com.example.jpa.comment.service.CommentService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("api/comments") +@RequiredArgsConstructor +public class CommentController { + + private final CommentService commentService; + + @PostMapping + public ResponseEntity create(@RequestBody CommentCreateRequest commentCreateRequest) { + + commentService.create(commentCreateRequest); + + return ResponseEntity.ok().build(); + } + + @GetMapping("/{id}") + public ResponseEntity findOne(@PathVariable("id") Long id) { + + CommentResponse commentResponse = commentService.findOne(id); + + return ResponseEntity.ok(commentResponse); + } + + @PatchMapping("/{id}") + public ResponseEntity update(@PathVariable("id") Long id, @RequestBody CommentUpdateRequest commentUpdateRequest) { + + commentService.update(id, commentUpdateRequest); + + return ResponseEntity.ok().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable("id") Long id) { + + commentService.delete(id); + + return ResponseEntity.ok().build(); + } + + @GetMapping + public ResponseEntity findAll() { + + List commentResponses = commentService.findAll(); + + return ResponseEntity.ok(commentResponses); + } +} diff --git a/src/main/java/com/example/jpa/comment/domain/Comment.java b/src/main/java/com/example/jpa/comment/domain/Comment.java new file mode 100644 index 0000000..36e3638 --- /dev/null +++ b/src/main/java/com/example/jpa/comment/domain/Comment.java @@ -0,0 +1,44 @@ +package com.example.jpa.comment.domain; + +import com.example.jpa.member.domain.Member; +import com.example.jpa.post.domain.Post; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "member") + private Member member; + + @ManyToOne + @JoinColumn(name = "post") + private Post post; + + private String content; + + @Builder + public Comment(Member member, Post post, String content) { + this.member = member; + this.post = post; + this.content = content; + } + + public Comment(Member member, String content) { + this.member = member; + this.content = content; + } + + public void update(String content) { + this.content = content; + } +} diff --git a/src/main/java/com/example/jpa/comment/dto/request/CommentCreateRequest.java b/src/main/java/com/example/jpa/comment/dto/request/CommentCreateRequest.java new file mode 100644 index 0000000..975df0c --- /dev/null +++ b/src/main/java/com/example/jpa/comment/dto/request/CommentCreateRequest.java @@ -0,0 +1,22 @@ +package com.example.jpa.comment.dto.request; + +import com.example.jpa.comment.domain.Comment; +import com.example.jpa.member.domain.Member; +import com.example.jpa.post.domain.Post; +import lombok.Getter; + +@Getter +public class CommentCreateRequest { + + private Long member; + private Long post; + private String content; + + public Comment toEntity(Member member, Post post) { + return Comment.builder() + .member(member) + .post(post) + .content(this.content) + .build(); + } +} diff --git a/src/main/java/com/example/jpa/comment/dto/request/CommentUpdateRequest.java b/src/main/java/com/example/jpa/comment/dto/request/CommentUpdateRequest.java new file mode 100644 index 0000000..a018a5a --- /dev/null +++ b/src/main/java/com/example/jpa/comment/dto/request/CommentUpdateRequest.java @@ -0,0 +1,10 @@ +package com.example.jpa.comment.dto.request; + +import lombok.Getter; + +@Getter +public class CommentUpdateRequest { + + private String content; + +} diff --git a/src/main/java/com/example/jpa/comment/dto/response/CommentResponse.java b/src/main/java/com/example/jpa/comment/dto/response/CommentResponse.java new file mode 100644 index 0000000..ef415d0 --- /dev/null +++ b/src/main/java/com/example/jpa/comment/dto/response/CommentResponse.java @@ -0,0 +1,20 @@ +package com.example.jpa.comment.dto.response; + +import com.example.jpa.comment.domain.Comment; +import lombok.Getter; + +@Getter +public class CommentResponse { + + private Long id; + private String content; + + public CommentResponse(Long id, String content) { + this.id = id; + this.content = content; + } + + public static CommentResponse from(Comment comment) { + return new CommentResponse(comment.getId(), comment.getContent()); + } +} diff --git a/src/main/java/com/example/jpa/comment/service/CommentService.java b/src/main/java/com/example/jpa/comment/service/CommentService.java new file mode 100644 index 0000000..13527ca --- /dev/null +++ b/src/main/java/com/example/jpa/comment/service/CommentService.java @@ -0,0 +1,61 @@ +package com.example.jpa.comment.service; + +import com.example.jpa.comment.domain.Comment; +import com.example.jpa.comment.dto.request.CommentCreateRequest; +import com.example.jpa.comment.dto.request.CommentUpdateRequest; +import com.example.jpa.comment.dto.response.CommentResponse; +import com.example.jpa.member.domain.Member; +import com.example.jpa.post.domain.Post; +import com.example.jpa.repository.CommentRepository; +import com.example.jpa.repository.MemberRepository; +import com.example.jpa.repository.PostRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class CommentService { + + private final CommentRepository commentRepository; + private final MemberRepository memberRepository; + private final PostRepository postRepository; + + @Transactional + public void create(CommentCreateRequest commentCreateRequest) { + Member member = memberRepository.findById(commentCreateRequest.getMember()).get(); + Post post = postRepository.findById(commentCreateRequest.getPost()).get(); + commentRepository.save(commentCreateRequest.toEntity(member, post)); + } + + @Transactional(readOnly = true) + public CommentResponse findOne(Long id) { + Comment comment = commentRepository.findById(id) + .orElseThrow(() -> new NoSuchElementException("댓글이 존재하지 않습니다.")); + + return CommentResponse.from(comment); + } + + @Transactional + public void update(Long id, CommentUpdateRequest commentUpdateRequest) { + Comment comment = commentRepository.findById(id) + .orElseThrow(() -> new NoSuchElementException("댓글이 존재하지 않습니다.")); + comment.update(commentUpdateRequest.getContent()); + } + + @Transactional + public void delete(Long id) { + commentRepository.deleteById(id); + } + + @Transactional(readOnly = true) + public List findAll() { + return commentRepository.findAll().stream() + .map(CommentResponse::from) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/example/jpa/member/controller/MemberController.java b/src/main/java/com/example/jpa/member/controller/MemberController.java index 16fb71f..a27c4c3 100644 --- a/src/main/java/com/example/jpa/member/controller/MemberController.java +++ b/src/main/java/com/example/jpa/member/controller/MemberController.java @@ -1,17 +1,15 @@ package com.example.jpa.member.controller; import com.example.jpa.member.dto.request.MemberCreateRequest; +import com.example.jpa.member.dto.request.MemberUpdateRequest; import com.example.jpa.member.dto.response.MemberResponse; import com.example.jpa.member.service.MemberService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequestMapping("api/members") @@ -35,4 +33,28 @@ public ResponseEntity findOne(@PathVariable("id") Long id) { return ResponseEntity.ok(memberResponse); } + + @PatchMapping("/{id}") + public ResponseEntity update(@PathVariable("id") Long id, @RequestBody MemberUpdateRequest memberUpdateRequest) { + + memberService.update(id, memberUpdateRequest); + + return ResponseEntity.ok().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable("id") Long id) { + + memberService.delete(id); + + return ResponseEntity.ok().build(); + } + + @GetMapping + public ResponseEntity findAll() { + + List memberResponses = memberService.findAll(); + + return ResponseEntity.ok(memberResponses); + } } diff --git a/src/main/java/com/example/jpa/member/domain/Member.java b/src/main/java/com/example/jpa/member/domain/Member.java index 92ca27b..356f9b6 100644 --- a/src/main/java/com/example/jpa/member/domain/Member.java +++ b/src/main/java/com/example/jpa/member/domain/Member.java @@ -1,14 +1,16 @@ package com.example.jpa.member.domain; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import com.example.jpa.comment.domain.Comment; +import com.example.jpa.post.domain.Post; +import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import java.util.ArrayList; +import java.util.List; + @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -20,12 +22,18 @@ public class Member { private String name; + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) + private List posts = new ArrayList<>(); + + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) + private List comments = new ArrayList<>(); + @Builder public Member(String name) { this.name = name; } - void update(String name) { + public void update(String name) { this.name = name; } } diff --git a/src/main/java/com/example/jpa/member/dto/request/MemberUpdateRequest.java b/src/main/java/com/example/jpa/member/dto/request/MemberUpdateRequest.java new file mode 100644 index 0000000..b98ce00 --- /dev/null +++ b/src/main/java/com/example/jpa/member/dto/request/MemberUpdateRequest.java @@ -0,0 +1,10 @@ +package com.example.jpa.member.dto.request; + +import lombok.Getter; + +@Getter +public class MemberUpdateRequest { + + private String name; + +} diff --git a/src/main/java/com/example/jpa/member/service/MemberService.java b/src/main/java/com/example/jpa/member/service/MemberService.java index fa8740b..2e259c8 100644 --- a/src/main/java/com/example/jpa/member/service/MemberService.java +++ b/src/main/java/com/example/jpa/member/service/MemberService.java @@ -2,10 +2,14 @@ import com.example.jpa.member.domain.Member; import com.example.jpa.member.dto.request.MemberCreateRequest; +import com.example.jpa.member.dto.request.MemberUpdateRequest; import com.example.jpa.member.dto.response.MemberResponse; import com.example.jpa.repository.MemberRepository; + +import java.util.List; import java.util.NoSuchElementException; -import java.util.Optional; +import java.util.stream.Collectors; + import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -29,5 +33,22 @@ public MemberResponse findOne(Long id) { return MemberResponse.from(member); } + @Transactional + public void update(Long id, MemberUpdateRequest memberUpdateRequest) { + Member member = memberRepository.findById(id) + .orElseThrow(() -> new NoSuchElementException("게시글이 존재하지 않습니다.")); + member.update(memberUpdateRequest.getName()); + } + + @Transactional + public void delete(Long id) { + memberRepository.deleteById(id); + } + @Transactional(readOnly = true) + public List findAll() { + return memberRepository.findAll().stream() + .map(MemberResponse::from) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/com/example/jpa/post/controller/PostController.java b/src/main/java/com/example/jpa/post/controller/PostController.java new file mode 100644 index 0000000..de1ce46 --- /dev/null +++ b/src/main/java/com/example/jpa/post/controller/PostController.java @@ -0,0 +1,60 @@ +package com.example.jpa.post.controller; + +import com.example.jpa.post.dto.request.PostCreateRequest; +import com.example.jpa.post.dto.request.PostUpdateRequest; +import com.example.jpa.post.dto.response.PostAllResponse; +import com.example.jpa.post.dto.response.PostResponse; +import com.example.jpa.post.service.PostService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("api/posts") +@RequiredArgsConstructor +public class PostController { + + private final PostService postService; + + @PostMapping + public ResponseEntity create(@RequestBody PostCreateRequest postCreateRequest) { + + postService.create(postCreateRequest); + + return ResponseEntity.ok().build(); + } + + @GetMapping("/{id}") + public ResponseEntity findOne(@PathVariable("id") Long id) { + + PostResponse postResponse = postService.findOne(id); + + return ResponseEntity.ok(postResponse); + } + + @PatchMapping("/{id}") + public ResponseEntity update(@PathVariable("id") Long id, @RequestBody PostUpdateRequest postUpdateRequest) { + + postService.update(id, postUpdateRequest); + + return ResponseEntity.ok().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable("id") Long id) { + + postService.delete(id); + + return ResponseEntity.ok().build(); + } + + @GetMapping + public ResponseEntity findAll() { + + List postResponses = postService.findAll(); + + return ResponseEntity.ok(postResponses); + } +} diff --git a/src/main/java/com/example/jpa/post/domain/Post.java b/src/main/java/com/example/jpa/post/domain/Post.java new file mode 100644 index 0000000..ee949ee --- /dev/null +++ b/src/main/java/com/example/jpa/post/domain/Post.java @@ -0,0 +1,44 @@ +package com.example.jpa.post.domain; + +import com.example.jpa.comment.domain.Comment; +import com.example.jpa.member.domain.Member; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Post { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "member") + private Member member; + + private String title; + private String content; + + @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) + private List comments = new ArrayList<>(); + + @Builder + public Post(Member member, String title, String content) { + this.member = member; + this.title = title; + this.content = content; + } + + public void update(String title, String content) { + this.title = title; + this.content = content; + } +} diff --git a/src/main/java/com/example/jpa/post/dto/request/PostCreateRequest.java b/src/main/java/com/example/jpa/post/dto/request/PostCreateRequest.java new file mode 100644 index 0000000..6501bb3 --- /dev/null +++ b/src/main/java/com/example/jpa/post/dto/request/PostCreateRequest.java @@ -0,0 +1,21 @@ +package com.example.jpa.post.dto.request; + +import com.example.jpa.member.domain.Member; +import com.example.jpa.post.domain.Post; +import lombok.Getter; + +@Getter +public class PostCreateRequest { + + private Long member; + private String title; + private String content; + + public Post toEntity(Member member) { + return Post.builder() + .member(member) + .title(this.title) + .content(this.content) + .build(); + } +} diff --git a/src/main/java/com/example/jpa/post/dto/request/PostUpdateRequest.java b/src/main/java/com/example/jpa/post/dto/request/PostUpdateRequest.java new file mode 100644 index 0000000..3b351a4 --- /dev/null +++ b/src/main/java/com/example/jpa/post/dto/request/PostUpdateRequest.java @@ -0,0 +1,11 @@ +package com.example.jpa.post.dto.request; + +import lombok.Getter; + +@Getter +public class PostUpdateRequest { + + private String title; + private String content; + +} diff --git a/src/main/java/com/example/jpa/post/dto/response/PostAllResponse.java b/src/main/java/com/example/jpa/post/dto/response/PostAllResponse.java new file mode 100644 index 0000000..3beda96 --- /dev/null +++ b/src/main/java/com/example/jpa/post/dto/response/PostAllResponse.java @@ -0,0 +1,22 @@ +package com.example.jpa.post.dto.response; + +import com.example.jpa.post.domain.Post; +import lombok.Getter; + +@Getter +public class PostAllResponse { + + private Long id; + private String title; + private String content; + + public PostAllResponse(Long id, String title, String content) { + this.id = id; + this.title = title; + this.content = content; + } + + public static PostAllResponse from(Post post) { + return new PostAllResponse(post.getId(), post.getTitle(), post.getContent()); + } +} diff --git a/src/main/java/com/example/jpa/post/dto/response/PostResponse.java b/src/main/java/com/example/jpa/post/dto/response/PostResponse.java new file mode 100644 index 0000000..4d92184 --- /dev/null +++ b/src/main/java/com/example/jpa/post/dto/response/PostResponse.java @@ -0,0 +1,29 @@ +package com.example.jpa.post.dto.response; + +import com.example.jpa.post.domain.Post; +import lombok.Getter; + +import java.util.List; + +@Getter +public class PostResponse { + + private Long id; + private String title; + private String name; + private String content; + private List comments; + + public PostResponse(Long id, String title, String name, String content, List comments) { + this.id = id; + this.title = title; + this.name = name; + this.content = content; + this.comments = comments; + } + + public static PostResponse from(Post post, String name, List comments) { + return new PostResponse(post.getId(), post.getTitle(), name, post.getContent(), comments); + } + +} diff --git a/src/main/java/com/example/jpa/post/service/PostService.java b/src/main/java/com/example/jpa/post/service/PostService.java new file mode 100644 index 0000000..d0062a4 --- /dev/null +++ b/src/main/java/com/example/jpa/post/service/PostService.java @@ -0,0 +1,65 @@ +package com.example.jpa.post.service; + +import com.example.jpa.comment.domain.Comment; +import com.example.jpa.member.domain.Member; +import com.example.jpa.post.domain.Post; +import com.example.jpa.post.dto.request.PostCreateRequest; +import com.example.jpa.post.dto.request.PostUpdateRequest; +import com.example.jpa.post.dto.response.PostAllResponse; +import com.example.jpa.post.dto.response.PostResponse; +import com.example.jpa.repository.CommentRepository; +import com.example.jpa.repository.MemberRepository; +import com.example.jpa.repository.PostRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class PostService { + + private final PostRepository postRepository; + private final MemberRepository memberRepository; + private final CommentRepository commentRepository; + + @Transactional + public void create(PostCreateRequest postCreateRequest) { + Member member = memberRepository.findById(postCreateRequest.getMember()).get(); + postRepository.save(postCreateRequest.toEntity(member)); + } + + @Transactional(readOnly = true) + public PostResponse findOne(Long id) { + Post post = postRepository.findById(id) + .orElseThrow(() -> new NoSuchElementException("게시글이 존재하지 않습니다.")); + List comments = commentRepository.findByPostID(id) + .stream() + .map(Comment::getContent) + .collect(Collectors.toList()); + + return PostResponse.from(post, post.getMember().getName(), comments); + } + + @Transactional + public void update(Long id, PostUpdateRequest postUpdateRequest) { + Post post = postRepository.findById(id) + .orElseThrow(() -> new NoSuchElementException("게시글이 존재하지 않습니다.")); + post.update(postUpdateRequest.getTitle(), postUpdateRequest.getContent()); + } + + @Transactional + public void delete(Long id) { + postRepository.deleteById(id); + } + + @Transactional(readOnly = true) + public List findAll() { + return postRepository.findAll().stream() + .map(PostAllResponse::from) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/example/jpa/repository/CommentRepository.java b/src/main/java/com/example/jpa/repository/CommentRepository.java new file mode 100644 index 0000000..802401c --- /dev/null +++ b/src/main/java/com/example/jpa/repository/CommentRepository.java @@ -0,0 +1,16 @@ +package com.example.jpa.repository; + +import com.example.jpa.comment.domain.Comment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface CommentRepository extends JpaRepository { + @Query("select new com.example.jpa.comment.domain.Comment(c.member, c.content) " + + "from Comment c " + + "where c.post.id = :id") + List findByPostID(@Param("id") Long id); + +} diff --git a/src/main/java/com/example/jpa/repository/PostRepository.java b/src/main/java/com/example/jpa/repository/PostRepository.java new file mode 100644 index 0000000..0a9f831 --- /dev/null +++ b/src/main/java/com/example/jpa/repository/PostRepository.java @@ -0,0 +1,7 @@ +package com.example.jpa.repository; + +import com.example.jpa.post.domain.Post; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PostRepository extends JpaRepository { +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f13d82c..7391553 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,7 +3,7 @@ server: spring: datasource: - url: jdbc:mysql://localhost:3306/jpa?serverTimezone=UTC + url: jdbc:mysql://localhost:3307/jpa?serverTimezone=UTC driver-class-name: com.mysql.cj.jdbc.Driver username: root password: mysql