-
[2주차]DB 설계(by JPA)SpringBoot 2023. 11. 12. 18:59
2주차 과제 시 작
ERD
게시판 하나에 여러 개의 댓글이 달릴 수 있기 때문에
Board(게시판)과 Comment(댓글) DB를 1:N 관계로 설계했다
Board DB
테이블명 Board(게시판) 속성 이름 데이터 타입 키 글 제목 title VARCHAR(40) 작성 시간 write_time DATETIME 수정 시간 edit_time DATETIME 내용 content VARCHAR(10000) 글 번호 b_id BIGINT PK 작성자 writer_b VARCHAR(20) 제목, 작성 시간, 수정 시간, 내용, 글ID, 글 작성자 정보를 저장하고, 글ID를 식별자로 사용했다.
Comment DB
테이블명 Comment(댓글) 속성 이름 데이터 타입 키 댓글 번호 BIGINT PK 글 번호 BIGINT FK - Board.글 번호 작성 시간 DATETIME 수정 시간 DATETIME 댓글 내용 VARCHAR(10000) 댓글 작성자 VARCHAR(20) 작성 시간, 수정 시간, 내용, 글ID(FK), 댓글ID, 댓글 작성자 정보를 저장하고, 댓글ID를 식별자로 사용했다.
글ID는 Board DB의 식별자를 외래키로 사용한다.
그러나!
SQL문으로 DB를 생성하는 것이 아닌, JPA로 DB에 자동으로 SQL을 작성하는 것이 이번 과제의 목표이므로
아래에서 관련 내용을 다뤄보도록 할 것이다
그 전에 앞서
지난 주에 연결해 둔 DB가 잘 작동되는지 확인해 보았다
먼저 프로젝트 구조
엔티티/리포지토리/테스트 폴더를 구분해서 폴더링 함
application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/GDSC?serverTimezone=Asia/Seoul spring.datasource.username=root spring.datasource.password=0000 spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=create spring.jpa.properties.hibernate.dialect.storage_engine=innodb
주로 SQL 관련 설정을 해주었다
build.gradle - dependencies
MySQL 및 lombok 등 관련 설정을 추가해줬다
엔티티 관련 코드(Board.java / Comment.java)
#Board.java package gdsctuk.sbbasic.sptingbootstudybasic.model; import lombok.Builder; import lombok.Getter; import jakarta.persistence.*; import java.time.LocalDateTime; @Getter @Entity @Table(name = "Board") public class Board { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(nullable = false, updatable = false) private Long b_id; @Column(length = 40, nullable = false) private String title; @Column(length = 10000) private String content; @Column(length = 20) private String writer_b; private LocalDateTime write_time; private LocalDateTime edit_time; @PrePersist public void prePersist() { this.write_time = LocalDateTime.now(); this.edit_time = this.write_time; } @PreUpdate public void preUpdate() { this.edit_time = LocalDateTime.now(); } @Builder public Board(Long b_id, String title, String content, String writer_b, LocalDateTime write_time, LocalDateTime edit_time) { this.b_id = b_id; this.title = title; this.content = content; this.writer_b = writer_b; this.write_time = write_time; this.edit_time = edit_time; } // 기본 생성자 public Board() { } }
# Comment.java package gdsctuk.sbbasic.sptingbootstudybasic.model; import jakarta.persistence.*; import lombok.Builder; import lombok.Getter; import java.time.LocalDateTime; import java.util.Date; @Getter @Entity @Table(name = "Comment") public class Comment { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) @Column(nullable = false, updatable = false) private Long c_id; @Column(nullable = false, updatable = false) private Long b_id; @Column(length = 20) private String writer_c; @Column(length = 10000) private String content; private LocalDateTime write_time; private LocalDateTime edit_time; @PrePersist public void prePersist() { this.write_time = LocalDateTime.now(); this.edit_time = this.write_time; } @PreUpdate public void preUpdate() { this.edit_time = LocalDateTime.now(); } @Builder public Comment(Long c_id, Long b_id, String writer_c, String content, LocalDateTime write_time, LocalDateTime edit_time) { this.c_id = c_id; this.b_id = b_id; this.writer_c = writer_c; this.content = content; this.write_time = write_time; this.edit_time = edit_time; } // 기본 생성자 public Comment() { } }
작성 시간 및 수정 시간에 대한 자동 설정은 다른 블로그 분의 코드를 참고하였다
(https://jforj.tistory.com/257)
리포지토리 관련 코드(BoardRepository, CommentRepository)
테스트 관련 코드 (BoardRepositoryTest, CommentRepositoryTest)
package gdsctuk.sbbasic.sptingbootstudybasic.model; import gdsctuk.sbbasic.sptingbootstudybasic.repository.BoardRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @SpringBootTest class BoardRepositoryTest { @Autowired private BoardRepository boardRepository; @Test @Transactional void saveBoard() { // Given Board board = Board.builder() .title("Test Title") .content("Test Content") .writer_b("Test Writer") .build(); // When Board savedBoard = boardRepository.save(board); // Then assertNotNull(savedBoard.getB_id()); // 식별자가 생성되었는지 확인 assertEquals("Test Title", savedBoard.getTitle()); assertEquals("Test Content", savedBoard.getContent()); assertEquals("Test Writer", savedBoard.getWriter_b()); assertNotNull(savedBoard.getWrite_time()); assertNotNull(savedBoard.getEdit_time()); } @Test @Transactional void findAllBoards() { // Given Board board1 = Board.builder().title("Title 1").content("Content 1").writer_b("Writer 1").build(); Board board2 = Board.builder().title("Title 2").content("Content 2").writer_b("Writer 2").build(); boardRepository.save(board1); boardRepository.save(board2); // When List<Board> boards = boardRepository.findAll(); // Then assertEquals(2, boards.size()); } }
package gdsctuk.sbbasic.sptingbootstudybasic.model; import gdsctuk.sbbasic.sptingbootstudybasic.model.Comment; import gdsctuk.sbbasic.sptingbootstudybasic.repository.CommentRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.List; import static gdsctuk.sbbasic.sptingbootstudybasic.model.Comment.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @SpringBootTest class CommentRepositoryTest { @Autowired private CommentRepository commentRepository; @Test @Transactional void saveComment() { // Given Comment comment = Comment.builder() .b_id(1L) .writer_c("Test Writer") .content("Test Content") .build(); // When Comment savedComment = commentRepository.save(comment); // Then assertNotNull(savedComment.getC_id()); // 식별자가 생성되었는지 확인 assertEquals(1L, savedComment.getB_id()); assertEquals("Test Writer", savedComment.getWriter_c()); assertEquals("Test Content", savedComment.getContent()); assertNotNull(savedComment.getWrite_time()); assertNotNull(savedComment.getEdit_time()); } @Test @Transactional void findCommentsByBId() { // Given Comment comment1 = Comment.builder().b_id(1L).writer_c("Writer 1").content("Content 1").build(); Comment comment2 = Comment.builder().b_id(1L).writer_c("Writer 2").content("Content 2").build(); commentRepository.save(comment1); commentRepository.save(comment2); // When List<Comment> comments = commentRepository.findAllByBId(1L); // Then assertEquals(2, comments.size()); } }
Test 코드는 특히 다른 분들 코드를 많이 참고했다...
BoardRepositoryTest에서 CommentRepositoryTest로 잘 넘어가는 걸 볼 수 있었고
test 또한 성공적으로 빌드됨을 볼 수 있다
그럼 이제 실제 MySQL에 DB가 생성되어 있는지 여부를 확인해 봐야 하는데
입력한 대로 테이블이 자동 구성되어 있다
MySQL에서 DataBase -> Reverse Engineering 을 통해서도 ERD를 그릴 수 있는데
관계는 따로 추가해 주었고, board id의 경우 comment DB에서 외래키로 참조하게 되는데
그 부분이 잘 표현되지 않은 것 같아서 추가로 그 부분을 수행해 봐야 할 것 같다
'SpringBoot' 카테고리의 다른 글
ch2 - Spring Data JPA (0) 2023.12.21 [5주차] 댓글 CRUD 구현 (0) 2023.12.04 [4주차] 지난 피드백 반영, Git에 코드 올리기 (0) 2023.11.30 [3주차] CRUD(by JPA) 설계 후 Postman으로 실행하기 (1) 2023.11.28 [1주차] 개발 환경 세팅(+ DB 연동) (0) 2023.11.06