데이터베이스 설계와 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;- 서브쿼리 결과를 다시 조인하기
게시글 정보와 댓글을 함께 조회하며, 댓글을 작성한 작성자 정보를 함께 조회
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
