diff --git a/README.md b/README.md new file mode 100644 index 0000000..532427e --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +스크린샷 2024-01-03 오후 4 01 30 + +api 문서 : https://documenter.getpostman.com/view/29836884/2s9YsFEEBq diff --git a/src/main/java/com/example/jpa/board/controller/BoardController.java b/src/main/java/com/example/jpa/board/controller/BoardController.java new file mode 100644 index 0000000..a4527a9 --- /dev/null +++ b/src/main/java/com/example/jpa/board/controller/BoardController.java @@ -0,0 +1,49 @@ +package com.example.jpa.board.controller; + +import com.example.jpa.board.dto.request.BoardCreateRequest; +import com.example.jpa.board.dto.request.BoardUpdateRequest; +import com.example.jpa.board.dto.response.BoardResponse; +import com.example.jpa.board.service.BoardService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("api/board") +@RequiredArgsConstructor +public class BoardController { + + private final BoardService boardService; + + @PostMapping + public ResponseEntity create(@RequestBody BoardCreateRequest boardCreateRequest) { + + boardService.create(boardCreateRequest); + + return ResponseEntity.ok().build(); + } + + @GetMapping("/{id}") + public ResponseEntity findOne(@PathVariable("id") Long id) { + + BoardResponse boardResponse = boardService.findOne(id); + + return ResponseEntity.ok(boardResponse); + } + + @PatchMapping("/{id}") + public ResponseEntity update(@PathVariable("id") Long id, @RequestBody BoardUpdateRequest boardUpdateRequest) { + + boardService.update(id, boardUpdateRequest); + + return ResponseEntity.ok().build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable("id") Long id) { + + boardService.delete(id); + + return ResponseEntity.ok().build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/jpa/board/domain/Board.java b/src/main/java/com/example/jpa/board/domain/Board.java new file mode 100644 index 0000000..7ffbe6e --- /dev/null +++ b/src/main/java/com/example/jpa/board/domain/Board.java @@ -0,0 +1,45 @@ +package com.example.jpa.board.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 Board { + @Id + @GeneratedValue + @Column(name = "board_id") + private Long id; + + private String title; + + private String body; + + @OneToMany(mappedBy = "board", cascade = CascadeType.REMOVE) + private List comments = new ArrayList<>(); + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @Builder + public Board(Member member, String title, String body) { + this.member = member; + this.title = title; + this.body = body; + } + + public void update(String title, String body) { + this.title = title; + this.body = body; + } +} diff --git a/src/main/java/com/example/jpa/board/dto/request/BoardCreateRequest.java b/src/main/java/com/example/jpa/board/dto/request/BoardCreateRequest.java new file mode 100644 index 0000000..dc8db36 --- /dev/null +++ b/src/main/java/com/example/jpa/board/dto/request/BoardCreateRequest.java @@ -0,0 +1,20 @@ +package com.example.jpa.board.dto.request; + +import com.example.jpa.board.domain.Board; +import com.example.jpa.member.domain.Member; +import lombok.Getter; + +@Getter +public class BoardCreateRequest { + private Long member; + private String title; + private String body; + + public Board toEntity(Member member) { + return Board.builder() + .member(member) + .title(this.title) + .body(this.body) + .build(); + } +} diff --git a/src/main/java/com/example/jpa/board/dto/request/BoardUpdateRequest.java b/src/main/java/com/example/jpa/board/dto/request/BoardUpdateRequest.java new file mode 100644 index 0000000..ebf1088 --- /dev/null +++ b/src/main/java/com/example/jpa/board/dto/request/BoardUpdateRequest.java @@ -0,0 +1,9 @@ +package com.example.jpa.board.dto.request; + +import lombok.Getter; + +@Getter +public class BoardUpdateRequest { + private String title; + private String body; +} diff --git a/src/main/java/com/example/jpa/board/dto/response/BoardResponse.java b/src/main/java/com/example/jpa/board/dto/response/BoardResponse.java new file mode 100644 index 0000000..b56b9de --- /dev/null +++ b/src/main/java/com/example/jpa/board/dto/response/BoardResponse.java @@ -0,0 +1,27 @@ +package com.example.jpa.board.dto.response; + +import com.example.jpa.board.domain.Board; +import lombok.Getter; + +import java.util.List; + +@Getter +public class BoardResponse { + private Long id; + private String title; + private String name; + private String body; + private List comments; + + public BoardResponse(Long id, String title, String name, String body, List comments) { + this.id = id; + this.title = title; + this.name = name; + this.body = body; + this.comments = comments; + } + + public static BoardResponse from(Board board, String name, List comments) { + return new BoardResponse(board.getId(), board.getTitle(), name, board.getBody(), comments); + } +} diff --git a/src/main/java/com/example/jpa/board/repository/BoardRepository.java b/src/main/java/com/example/jpa/board/repository/BoardRepository.java new file mode 100644 index 0000000..150739e --- /dev/null +++ b/src/main/java/com/example/jpa/board/repository/BoardRepository.java @@ -0,0 +1,7 @@ +package com.example.jpa.board.repository; + +import com.example.jpa.board.domain.Board; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BoardRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/jpa/board/service/BoardService.java b/src/main/java/com/example/jpa/board/service/BoardService.java new file mode 100644 index 0000000..a05b344 --- /dev/null +++ b/src/main/java/com/example/jpa/board/service/BoardService.java @@ -0,0 +1,58 @@ +package com.example.jpa.board.service; + +import com.example.jpa.board.domain.Board; +import com.example.jpa.board.dto.request.BoardCreateRequest; +import com.example.jpa.board.dto.request.BoardUpdateRequest; +import com.example.jpa.board.dto.response.BoardResponse; +import com.example.jpa.board.repository.BoardRepository; +import com.example.jpa.comment.domain.Comment; +import com.example.jpa.comment.dto.request.CommentCreateRequest; +import com.example.jpa.comment.repository.CommentRepository; +import com.example.jpa.member.domain.Member; +import com.example.jpa.member.repository.MemberRepository; +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 BoardService { + + private final BoardRepository boardRepository; + private final MemberRepository memberRepository; + private final CommentRepository commentRepository; + + @Transactional + public void create(BoardCreateRequest boardCreateRequest) { + Member member = memberRepository.findById(boardCreateRequest.getMember()).get(); + boardRepository.save(boardCreateRequest.toEntity(member)); + } + + @Transactional(readOnly = true) + public BoardResponse findOne(Long id) { + Board board = boardRepository.findById(id) + .orElseThrow(() -> new NoSuchElementException("게시글이 존재하지 않습니다.")); + List comments = commentRepository.findByBoardId(id) + .stream() + .map(Comment::getBody) + .collect(Collectors.toList()); + + return BoardResponse.from(board, board.getMember().getName(), comments); + } + + @Transactional + public void update(Long id, BoardUpdateRequest boardUpdateRequest) { + Board board = boardRepository.findById(id) + .orElseThrow(() -> new NoSuchElementException("게시글이 존재하지 않습니다.")); + board.update(boardUpdateRequest.getTitle(), boardUpdateRequest.getBody()); + } + + @Transactional + public void delete(Long id) { + boardRepository.deleteById(id); + } +} \ No newline at end of file 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..6e2b425 --- /dev/null +++ b/src/main/java/com/example/jpa/comment/controller/CommentController.java @@ -0,0 +1,62 @@ +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.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/comment") +public class CommentController { + + private final CommentService commentService; + + @PostMapping("/create") + public ResponseEntity create(@RequestBody CommentCreateRequest commentCreateRequest) { + Long saveResult = commentService.create(commentCreateRequest); + if (saveResult != null) { + return new ResponseEntity<>(HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @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..75d9398 --- /dev/null +++ b/src/main/java/com/example/jpa/comment/domain/Comment.java @@ -0,0 +1,41 @@ +package com.example.jpa.comment.domain; + +import com.example.jpa.board.domain.Board; +import com.example.jpa.member.domain.Member; +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(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @Column + private String body; + + /* Board:Comment = 1:N */ + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "board_id") + private Board board; + + @Builder + public Comment(Member member, String body, Board board) { + this.member = member; + this.body = body; + this.board = board; + } + + public void update(String body) { + this.body = body; + } +} 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..9c0af87 --- /dev/null +++ b/src/main/java/com/example/jpa/comment/dto/request/CommentCreateRequest.java @@ -0,0 +1,21 @@ +package com.example.jpa.comment.dto.request; + +import com.example.jpa.board.domain.Board; +import com.example.jpa.comment.domain.Comment; +import com.example.jpa.member.domain.Member; +import lombok.Getter; + +@Getter +public class CommentCreateRequest { + private Long member; + private Long board; + private String body; + + public Comment toEntity(Member member, Board board) { + return Comment.builder() + .member(member) + .board(board) + .body(this.body) + .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..575a388 --- /dev/null +++ b/src/main/java/com/example/jpa/comment/dto/request/CommentUpdateRequest.java @@ -0,0 +1,8 @@ +package com.example.jpa.comment.dto.request; + +import lombok.Getter; + +@Getter +public class CommentUpdateRequest { + private String body; +} 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..b7d6581 --- /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 body; + + public CommentResponse(Long id, String body) { + this.id = id; + this.body = body; + } + + public static CommentResponse from(Comment comment) { + return new CommentResponse(comment.getId(), comment.getBody()); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/jpa/comment/repository/CommentRepository.java b/src/main/java/com/example/jpa/comment/repository/CommentRepository.java new file mode 100644 index 0000000..2e8f508 --- /dev/null +++ b/src/main/java/com/example/jpa/comment/repository/CommentRepository.java @@ -0,0 +1,11 @@ +package com.example.jpa.comment.repository; + +import com.example.jpa.comment.domain.Comment; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Arrays; +import java.util.List; + +public interface CommentRepository extends JpaRepository { + List findByBoardId(Long boardId); +} 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..005470b --- /dev/null +++ b/src/main/java/com/example/jpa/comment/service/CommentService.java @@ -0,0 +1,69 @@ +package com.example.jpa.comment.service; + +import com.example.jpa.board.domain.Board; +import com.example.jpa.board.repository.BoardRepository; +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.comment.repository.CommentRepository; +import com.example.jpa.member.domain.Member; +import com.example.jpa.member.repository.MemberRepository; +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.Optional; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class CommentService { + + private final CommentRepository commentRepository; + private final MemberRepository memberRepository; + private final BoardRepository boardRepository; + + @Transactional + public Long create(CommentCreateRequest commentCreateRequest) { + + Member member = memberRepository.findById(commentCreateRequest.getMember()).get(); + Board board = boardRepository.findById(commentCreateRequest.getBoard()).get(); + Optional optionalBoardEntity = boardRepository.findById(commentCreateRequest.getBoard()); + + if (optionalBoardEntity.isPresent()) { + return commentRepository.save(commentCreateRequest.toEntity(member, board)).getId(); + } else { + return null; + } + } + + @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()); + } +} \ No newline at end of file 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..2e70bd7 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,13 @@ 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.*; @RestController @RequestMapping("api/members") @@ -35,4 +31,21 @@ 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}") + @PatchMapping("/{id}") + public ResponseEntity delete(@PathVariable("id") Long id) { + + memberService.delete(id); + + return ResponseEntity.ok().build(); + } } 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..1d26488 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.board.domain.Board; +import com.example.jpa.comment.domain.Comment; +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) @@ -16,16 +18,23 @@ public class Member { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "member_id") private Long id; private String name; + @OneToMany(mappedBy = "member", cascade = CascadeType.REMOVE) + private List boards = new ArrayList<>(); + + @OneToMany(mappedBy = "member", cascade = CascadeType.REMOVE) + 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..37cf0e4 --- /dev/null +++ b/src/main/java/com/example/jpa/member/dto/request/MemberUpdateRequest.java @@ -0,0 +1,16 @@ +package com.example.jpa.member.dto.request; + +import com.example.jpa.member.domain.Member; +import lombok.Getter; + +@Getter +public class MemberUpdateRequest { + + private String name; + + public Member toEntity() { + return Member.builder() + .name(this.name) + .build(); + } +} diff --git a/src/main/java/com/example/jpa/repository/MemberRepository.java b/src/main/java/com/example/jpa/member/repository/MemberRepository.java similarity index 85% rename from src/main/java/com/example/jpa/repository/MemberRepository.java rename to src/main/java/com/example/jpa/member/repository/MemberRepository.java index 6056cc0..848c8d2 100644 --- a/src/main/java/com/example/jpa/repository/MemberRepository.java +++ b/src/main/java/com/example/jpa/member/repository/MemberRepository.java @@ -1,4 +1,4 @@ -package com.example.jpa.repository; +package com.example.jpa.member.repository; import com.example.jpa.member.domain.Member; import org.springframework.data.jpa.repository.JpaRepository; 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..df49953 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,11 @@ 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 com.example.jpa.member.repository.MemberRepository; import java.util.NoSuchElementException; -import java.util.Optional; + import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -29,5 +30,19 @@ 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()); + + memberRepository.save(member); + } + @Transactional + public void delete(Long id) { + Member member = memberRepository.findById(id) + .orElseThrow(() -> new NoSuchElementException("멤버가 존재하지 않습니다.")); + memberRepository.delete(member); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f13d82c..d4009c3 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,10 +3,10 @@ server: spring: datasource: - url: jdbc:mysql://localhost:3306/jpa?serverTimezone=UTC + url: ${DB_URL} driver-class-name: com.mysql.cj.jdbc.Driver - username: root - password: mysql + username: ${DB_USERNAME} + password: ${DB_PASSWORD} jpa: hibernate: