springboot - blog project (22)

Yuri Lee·2020년 8월 24일
0

springboot - blog project

목록 보기
12/49

스프링부트 강좌 22강(블로그 프로젝트) - 연관관계의 주인

세가지 클래스로 테이블을 만들긴 했다. 하지만 아직 완성되지 않았다. 연관관계의 주인을 제대로 이해하지 못했기 때문이다.

연관관계의 주인 = FK를 가진 오브젝트

웹 프로그램을 실행하면 화면이 나올 것이다.

  1. 게시글
  2. 게시글
  3. 게시글

previous btn / next btn

이 화면이 main 페이지라고 하자. 그리고 밑에는 p/n 버튼이 있다고 하자. n btn을 누르면 다음 페이지로 넘어갈 것이다. 그럼 4,5,6 게시글을 보게 될 것이다. 이 페이지에서 1 번 게시글을 클릭하면 새로운 화면으로 갈 것이다. 상세보기 화면으로...

작성자: love
제목: 오늘은 즐거운 날
내용:
댓글 : ~~
ssar: 부러워요!
cos: 저도 함께 하고 싶어요!

love 유저네임 정보가 있다. 유저네임 정보는 누가 들고 있는가?자바 프로그램에서는 유저 오브젝트, 디비에서는 user 테이블이 ~

제목, 내용은 board 라는 테이블이 ~

댓글은 reply 테이블이 ~

우리가 orm을 사용하기 전에 jpa 를 사용하기 전, 즉 예전 방식을 사용하고 있었다면(MyBatis과 같은) 이 3개를 조인을 해서 select 해서 해당 페이지의 데이터를 다 들고 왔을 것이다.

근데 orm 방식을 사용하면 굉장히 간단한 게... board table 만 select 하면 된다. 내가 board 테이블을 select 하게 되면

select * from Board where Id = 1;

자바프로그램 ---- JPA ---- DB

그럼 jpa 는 자바프로그램과 디비 사이에서 어떤 역할을 한다. 내가 자바 프로그램에서 이 쿼리를 날리고 싶다. 요청을 할 것이다. 그럼 jpa가 board라는 모델이 하나 필요하구나..id가 1번이 필요하구나..인식을 하고 똑같은 쿼리문을 만드는 게 아니라

board가 user라는 오브젝트를 들고 있다. 그럼 id 값은 board select 해서 가져올 수 있고.. title 값도 , content, counte, createDate 값도 다 들고 올 수 있는데 user은 ....user table과 조인을 해서 들고 와야 할 정보잖아..!! 라고 인식을 한다.

Board(Id=1) 가 들고 있는 user 정보 오브젝트 때문에 jpa 가 조인문을 날린다. user + board 를 조인한 셀렉트 문을 던진다. 그럼 데이터베이스가 거기에 대한 정보를 다시 jpa 에게 돌려주는데..그럼 board object와 , board 오브젝트 내에 user 오브젝트 정보가 쑥 들어오게 된다.

결국 내가 원하는 것은 board인데 보드를 셀렉트 하게 되면 유저정보를 같이 주게 된다.

어떻게 보면 굉장히 좋은 기술인데 또 어떻게 보면 필요 없을 수도 있다.

만약 상세보기 페이지에서 보드를 셀렉트를 했다. 보드를 셀렉트 해서 요청을 한다. 그럼 유저정보를 같이 준다. 왜 ? 보드가 유저 오브젝트를 들고 있기 때문에.. 그럼 reply정보는 없다. 그럼 한번더 셀렉트를 해야 한다. 그래서...

어떤 코드가 한줄 더 필요하냐면...? 내가 board를 셀렉트 할때는 유저 뿐만 아니라 reply정보도 필요하다.

보드를 셀렉트 하려고 하는데 보드 안에 유저정보 + 리플리 정보도 필요하다. 그럼 조인문을 이 세개를 조인해서 디비에게 날린다. 그럼 디비가 이 세개의 정보를 돌려준다.

하나의 게시글에는
Board

  • user (1개 o)
  • reply (1개 x)
    한명의 유저가 글을 쓸 수 있다. 두명의 유저가 하나의 게시글을 쓰는 게 아니다. 하나의 게시글은 몇개의 답변을 갖고 있을까?..1개일 수도, 천개일 수도 있다.

그럼 조인을 해서 reply 정보를 1개만 들고오면 안된다. user는 1개여도 되지만...그럼 reply는 1개이면 안되기 때문에 List가 들어가야 한다.

하나의 게시글은 여러개의 댓글을 가질 수 있기 때문에 OneToMany가 된다.

@OneToMany
//@JoinColumn(name="replyId") 이게 필요가 없다
private List<Reply> reply;

FK가 필요 없기 때문이다.

id | title | content | userId | replyId |
createDate

1 안녕 반가워 2 1,2 2020.5.17

답변이 1번 답변이 들어왔다, 근데 또 답변이 들어왔다고 해서 1,2 라고 적을 수 없다. 이러면 1정규화에 위배되는 것이다.

따라서 여기에는 FK 필요가 필요없다. 따라서 데이터베이스에는 만들어지면 안된다.

@OneToMany(mappedBy = "board") //mappedBy 연관관계의 주인이 아니다. (난 FK가 아니다). DB에 컬럼을 만들지 마시오. 
	private List<Reply> reply;

FK는 여기 있는 게 아니라 replay table의 board가 FK라는 의미이다.

Board table을 select 하면 user 정보는 가져온다. 왜? 한건밖에 없으니까 바로 조인해서 가져온다는 것이다. OneToMany는 기본 전략이 fetch 타입이 eager이 아니다. 왜냐면..이건 엄청나게 많은 건수가 될 수 있기 때문이다. 그래서 기본 패치 전략이 lazy전략이 된다.

보드를 선택하면 상세보기 화면을 볼 수 있다. 그때 무조건 작성자 정보가 필요하니까 eager 로 해야 한다.
reply도 무조건 들고와야 한다.

만약 상세보기 할때 이 댓글이 보이는 게 아니라 ui가

작성자: lov
제목: ~
내용: ~
댓글: 펼치기 버튼

user 1건, board 1건, 댓글은 필요하지 않다. 펼치기 버튼을 클릭하기 전에는 ... 댓글은 필요하지 않다. 이렇게 되면 eager 전략을 사용할 필요가 없다. 필요할 때 당겨오기 때문에 lazy 전략을 사용하면 된다.

근데 우리가 처음에 정했던 UI로 진행할 경우 board select 할때 user, reply 정보를 모두 들고 와야 한다. 따라서 reply를 eager전략으로 바꿔줘야 한다.

package com.yuri.blog.model;

import java.sql.Timestamp;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder //빌더 패턴
@Entity //orm,  데이터베이스에 맵핑을 시켜주는 클래스이다라고 명시해주는 어노테이션을 가까이 명시하는 게 좋음
public class Board {
	
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)	//auto_increment
	private int id;
	
	@Column(nullable = false, length=100)
	private String title;
	
	@Lob //대용량 데이터를 사용할 떄
	private String content; // 섬머노트 라이브러리 <html>태그가 섞여서 디자인이 됨
	
	
	@ColumnDefault("0") //int 값이니까 ' ' 없이 사용
	private int count; // 조회수
	
	@ManyToOne(fetch = FetchType.EAGER) //Many = Board, User = One
	@JoinColumn(name="userId") //실제로 db에 만들어질 때는 userId라는 이름으로 만들어 질 것이다. 
	private User user; //DB는 오브젝트를 저장할 수 없다. FK, 자바는 오브젝트를 저장할 수 있다. 
	
	@OneToMany(mappedBy = "board", fetch = FetchType.EAGER) //mappedBy 연관관계의 주인이 아니다. (난 FK가 아니다). DB에 컬럼을 만들지 마시오. 
	private List<Reply> reply;
	
	@CreationTimestamp
	private Timestamp createDate;
	
}

-이 글은 유투버 겟인데어의 스프링 부트 강좌를 바탕으로 정리한 내용입니다.-

profile
Step by step goes a long way ✨

0개의 댓글