데이터베이스 설계와 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 쿼리와 서브쿼리 활용
- 게시글, 작성자 정보 조회
SELECT *
FROM board_tb bt
inner join user_tb ut on bt.user_id = ut.id;

- 특정 게시글 조회
SELECT *
FROM board_tb bt
inner join user_tb ut on bt.user_id = ut.id
where bt.id=5;

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

- 인라인 뷰 활용
서브쿼리를 활용하여 필터링 합니다.
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
);

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
);

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;

이미 선택이 완료되었기 때문에 where 절에서
u_id
라는 별칭 사용이 가능하다.- 서브쿼리 결과를 다시 조인하기
게시글 정보와 댓글을 함께 조회하며, 댓글을 작성한 작성자 정보를 함께 조회
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;

USER_ID 는 댓글을 작성한 유저 아이디이다.
화면 출력을 위해서는 id 보다 작성자 이름이 필요하다.
- 다중 조인 및 댓글 정보 조회
각 컬럼 마다 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;


댓글이 3개 있으므로 내용도 세번 출력이 됨. but 한번만 필요하다.

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;

LEFT OUTER JOIN
을 사용하여 결과의 유무에 상관없이 일정한 정보를 뽑을 수 있다.다음 시간에는
@Query
와 join fetch
를 사용하여 댓글 기능을 구현해보겠습니다
SpringBoot - v3 1. https://inblog.ai/hj/v3-시작-27809 (개발환경 설정 및 post 맨 이용한 api 테스트) 2. https://inblog.ai/hj/v3-springboot-블로그-만들기-2-28708 (댓글 엔티티 생성 및 양방향 매핑)
Share article