[v3] SpringBoot 블로그 만들기 -2

댓글 기능을 위한 엔티티 생성 및 양방향 매핑, 그리고 쿼리문 테스트
HootJem's avatar
Sep 04, 2024
[v3] SpringBoot 블로그 만들기 -2
데이터베이스 설계와 JPA: 테이블 쪼개기와 연관관계 매핑

1. 테이블 쪼개기

db 설계 시 한 테이블에 모든 컬럼을 넣는것이 아닌 일정한 근거에 따라 테이블을 분리해야 한다.
예를들어, 필드를 추가하고 싶은데 오브젝트로 표현해야 할 때 → 댓글번호, 댓글내용, 댓글시간, 댓글 주인, 댓글 게시글 번호
내가 필드를 추가하고싶은데 컬렉션으로 표현해야 할 때
 
테이블을 분리하면 데이터 관리에 용이하다.

2. 연관관계 설정

유저, 게시글, 댓글간의 관계를 설정해야한다.
💡
유저(1) 게시글(N) → 1:N
유저(1) 댓글(N) → 1:N
게시글(1) 댓글(N) → 1:N 한 명의 유저는 게시글, 댓글 을 여러개 작성할 수 있고 하나의 게시글은 여러개의 댓글을 가진다.
N 쪽에 외래키가 들어가야한다

3. 엔티티

연관관계 설정을 참고하면 댓글은 유저와 게시글의 정보를 갖고 있어야한다.
fetch = FetchType.LAZY 을 사용할 것이다.
@Setter @Getter @Table(name = "reply_tb") @NoArgsConstructor @Entity public class Reply { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String comment; //댓글 내용 @ManyToOne(fetch = FetchType.LAZY) private User user; @ManyToOne(fetch = FetchType.LAZY) private Board board; @CreationTimestamp private Timestamp createdAt; }
 

3.1 게시글 조회 쿼리

현재 게시글 조회를 위한 쿼리는 다음과 같다.
@Query("select b from Board b join fetch b.user u where b.id=:id") Optional<Board> mfindById(@Param("id") Integer id);
댓글을 조인하고 있지 않기 때문에 댓글 데이터가 나오지 않지만 만약, 조인한다고 해도 보드객체엔 Reply 에관한 객체가 없어서 값 리턴이 불가능하다.
따라서 Board 엔티티에 댓글 컬렉션을 추가해야한다.
 

4. 게시글 엔티티 수정(양방향 매핑)

package org.example.springv3.board; import jakarta.persistence.*; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.example.springv3.reply.Reply; import org.example.springv3.user.User; import org.hibernate.annotations.CreationTimestamp; import java.sql.Timestamp; import java.util.List; @NoArgsConstructor @Setter @Getter @Table(name = "board_tb") @Entity public class Board { @GeneratedValue(strategy = GenerationType.IDENTITY) @Id // PK 설정 private Integer id; @Column(nullable = false) private String title; @Column(nullable = false) private String content; @CreationTimestamp private Timestamp createdAt; // fk @ManyToOne(fetch = FetchType.LAZY) private User user; @OneToMany(mappedBy = "board") private List<Reply> replies; @Builder public Board(Integer id, String title, String content, Timestamp createdAt, User user) { this.id = id; this.title = title; this.content = content; this.createdAt = createdAt; this.user = user; } }
@OneToMany(mappedBy = "board") 어노테이션을 통해 댓글 리스트를 추가하고 댓글과 게시글 간의 양방향 매핑을 설정할 수 있다.

매핑설정이 완료 되었다면 db(=H2) 에서 확인을 해 본다.

SQL 쿼리와 서브쿼리 활용

  1. 게시글, 작성자 정보 조회
SELECT * FROM board_tb bt inner join user_tb ut on bt.user_id = ut.id;
notion image
 
  1. 특정 게시글 조회
SELECT * FROM board_tb bt inner join user_tb ut on bt.user_id = ut.id where bt.id=5;
notion image
 
2-1. 게시글 조회 시 컬럼 선택
SELECT bt.id, bt.title, bt.content, ut.id user_id, ut.username FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id where bt.id = 5
notion image
 
  1. 인라인 뷰 활용
    1. 서브쿼리를 활용하여 필터링 합니다.
SELECT * FROM ( SELECT bt.id, bt.title, bt.content, ut.username FROM board_tb bt inner join user_tb ut on bt.user_id = ut.id where bt.id=5 );
notion image
select * from ( SELECT bt.id, bt.title, bt.content, ut.id u_id, ut.username FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id );
notion image
 
3-1. 서브쿼리에서 컬럼 별칭 사용
select * from ( SELECT bt.id, bt.title, bt.content, ut.id u_id, ut.username FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id ) where u_id=2;
notion image
이미 선택이 완료되었기 때문에 where 절에서 u_id 라는 별칭 사용이 가능하다.
 
  1. 서브쿼리 결과를 다시 조인하기
    1. 게시글 정보와 댓글을 함께 조회하며, 댓글을 작성한 작성자 정보를 함께 조회
SELECT * FROM ( SELECT bt.id, bt.title, bt.content, ut.username FROM board_tb bt inner join user_tb ut on bt.user_id = ut.id where bt.id=5 )t1 inner join reply_tb t2 on t1.id = t2.board_id;
notion image
USER_ID 는 댓글을 작성한 유저 아이디이다. 화면 출력을 위해서는 id 보다 작성자 이름이 필요하다.
 
  1. 다중 조인 및 댓글 정보 조회
각 컬럼 마다 Id 가 존재하여 3단 조인 하니 에러가 발생함. 따라서 서브쿼리 활용하지 않고
작성하다.
SELECT * FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id INNER JOIN reply_tb rt on bt.id = rt.board_id INNER JOIN user_tb rut on rut.id = rt.user_id where bt.id = 5;
notion image
 
notion image
댓글이 3개 있으므로 내용도 세번 출력이 됨. but 한번만 필요하다.
 
notion image
inner 조인 했기 때문에 댓글이 없다면 결과가 나오지 않는다.
따라서 댓글은 inner join 이 아닌 아우터 조인이 들어가야한다.
 
 
SELECT * FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id LEFT OUTER JOIN reply_tb rt on bt.id = rt.board_id LEFT OUTER JOIN user_tb rut on rut.id = rt.user_id where bt.id = 2;
notion image
LEFT OUTER JOIN 을 사용하여 결과의 유무에 상관없이 일정한 정보를 뽑을 수 있다.
 
다음 시간에는 @Queryjoin fetch를 사용하여 댓글 기능을 구현해보겠습니다

SpringBoot - v3 1. https://inblog.ai/hj/v3-시작-27809 (개발환경 설정 및 post 맨 이용한 api 테스트) 2. https://inblog.ai/hj/v3-springboot-블로그-만들기-2-28708 (댓글 엔티티 생성 및 양방향 매핑)
 
Share article

[HootJem] 개발 기록 블로그