Wanna be Brilliant Full-Stack Developer
SpringBoot Blog 만들기 연관관계의 주인 본문
연관관계의 주인에 대해서 이해를 하고 클래스를 완벽하게 설계를 할 수 있다.
연관관계의 주인이라는것은 무엇을 말하는가?
FK를 누가 가졌는가를 말하는것이다.
우리가 웹프로그램을 실행을 하면 이런 화면이 나올것이다.
이 화면을 메인페이지라고 한다면! previeous는 이전 next는 다음 게시글을 보게 될것이다.
저 게시글을 클릭하게 되면 새로운 화면으로 갈것이다.
이 화면은 상세보기 화면이 될것이다.
상세 보기 화면에는 위에 작성자 : love
제목 : 오늘 은 즐거운날
내용 : ~~~ 라고 적혀있는 게시글이 있고
밑에는 댓글이 있다.
ssar이라는 애가 부러워요 댓글을 남겼다.
cos라는 애도 저도 가고싶어요라고 댓글을 남김
상세보기를 보면 love라는게 있으니까 어떤 username인지 username정보가 있다.
username을 누가 들고 있는가? user라는 테이블이들고있고 자바에서는 User오브젝트가 가지고 있는것이다.
제목과 내용은? Board라는 테이블이 가지고 있는것이고 자바에서는 Board 오브젝트가 가지고 있는것이다.
댓글은 Reply가 들고 있다.
우리가 ORM 이나 JPA를 쓰기 전에는 이 세개를 join을 해서 Select을 해서 해당 페이지에 데이터를
다가져왔을것인데 ORM방식을 쓰게 되면
이 Board테이블만 셀렉트 하면 된다.
Board테이블만 셀렉트하게 되면 Select * from Board where Id = 1 ;을 한다면
JPA가 무슨 일을 하는가?
자바프로그램과 데이터베이스 사이에서 어떤 역할을 하는데?
내가 자바프로그램에서 셀렉트해서 데이터베이스 한테 날리고 싶다.
날리면 JPA를 통해서 DB한테 요청을 한다.
데이터베이스 한테 이 쿼리를 날리는것이 아니라 JPA가 이 Board 모델에 id가 1번인게 필요하구나
라는것을 인식하고 똑같은 쿼리문을 만드는것이 아니라
Board가 멀 들고 있는가 하면? User라는 오브젝트를 들고 있다.
다른건 다 들고 올수 있는데 user는 Board만 셀렉트 한다고 들고 올수있는게 아니라
user테이블과 조인해야 들고 올수있는건데 라고 인식을 하고
Board가 들고 있는 user 오브젝트 정보 떄문에 JPA가 내가 요청한 저 쿼리를 날리는것이아니라
JOIN문을 날린다. JOIN문을 User + Board를 조인한 셀렉트 문을 날린다.
그러면 데이터베이스가 그것에 대한 정보를 JPA한테 돌려주는데
JPA는 어떤 정보를 가지고 있는가?
Board오브젝트 내부에 user오브젝트 정보가 쏙 들어오게 된다.
결국 내가 원하는건 Board인데 Board를 셀렉트하게 되면 user정보를 같이 주게 된다.
왜냐하면 User오브젝트를 들고 있기 때문이다.
어떻게 보면 좋을 수 있지만 어떻게 보면 필요하지 않을 수 있다.
상세 보기 페이지를 보면
Board를 셀렉트 해가지고 Join을 하지 않고 Board만 셀렉트를 하면 User정보를 같이 준다.
왜냐하면 board가 user정보를 가지고 있기 떄문이다.
reply정보는 가지고 있지 않기 떄문에 안준다. 한번더 셀렉트를 해야한다.
그래서 어떤 코드가 한줄 필요한가?
내가 Board를 셀렉트 할떄 user뿐만이 아니라 reply도 필요하다.
이제 Reply가 있기 때문에 Board를 셀렉트 할려고하는데? 이 board안에 user정보 플러스 reply정보가 있다.
그러면 Join문을 이 세개를 join해서 데이터베이스에 던진다.
이 세개에 대한 정보를 돌려주기 떄문에 Board는 user와 reply를 가지게 된다.
그러면 보자 하나의 게시글에는 한명의 유저가 쓸 수 있다.
한명의 유저가 하나의 게시글을 쓰는거지 두명의 유저가 하나의 게시글을 쓰는것이 아니다.
하나의 게시글은? 몇개의 답글을 가지고 있는가? 하나일 수있고 100개일 수 도 있다.
상세보기를 보면 하나의 게시글에는 한명의 작성자가 있고
답변은 한개가 될수 있고 천개가 될 수 있다.
이 관계가 보이는가?
그러면 Join으로 해서 user는 하나만가져와도 되지만 reply하나만 가져오는것이 아니라 한개면 안된다.
그렇기 떄문에 List가 들어간다 컬렉션이 되어야한다.
private List<Reply> reply;
를 해놓으면 애는 여러건이 될 수 있다.
그다음에 중요한것이 관계를 봐야한다.
하나의 게시글은 여러개의 답변을 둘 수 있다.
@OneToMany
private List<Reply> reply;
그렇기 떄문에 OneToMany가 된다.
그다음에는 JoinColumn(name = "replyId")가 필요 없다.
왜냐하면 Mysql 테이블이 replyId 포린키가 필요 없기 때문이다.
왜냐하면 만약에 저게 만들어진다고 치면 테이블은
어떤 Board라는 테이블이 있는데 Board라는 테이블에 Id, title , content, userId, createDate 이렇게 있을것이다.
게시글 하나 적으면 첫번쨰 게시글의 제목이 안녕 내용은 반가워 userId가 두번째 유저가 적었으면 2,
시간은 2020년 5월 17일이 들어온다고 해보자.
만약에 reply라는 포린키 ReplyId가 들어오면 말이 안된다.
하나의 게시글에 게시글 답변에 100개가 될수도있고 1000개가 될수도있는데 만약에 1번답변이 들어왔는데
하나 더들어오면 1,2라고 적을 수는 없기 떄문에 데이터베이스에서는 1정규화가 꺠진것이다.
데이터베이스는 하나의 컬럼은 하나의 원자성만 가진다.
,해서 값을 가질 수 없다.
애는 reply에 포린키를 가지는것이 아니라 Board에는 포린키가 필요 없다.
그러면 데이터베이스에는 JoinColum은 만들 필요가 없다.
오로지 Board테이블을 셀렉트 할떄 join해서 reply에만 넣어달라고 하면 된다.
그러면 무엇을 적어야하는가?
@OneToMany(mappedBy = "board")
Reply테이블에 있는 board를 우선 넣어준다.
이게 무슨말인가? mappedBy가 적혀져 있으면 연관관계의 주인이 아니다 ( 난 FK가 아니다)
그러면 DB에 컬럼을 만들지 말라는것이다.
왜냐하면 나의 주인은
FK는 Board테이블에 있는것이 아니라 Reply테이블에 있는 board가 FK이기 떄문이다.
ID , Content, userId, boardId
이렇게 만들어져있다.
1번 답변은 좋아요 , userId는 2번이고 boardId는 첫번째 게시글의 답변 이라고 1번이라고 적혀있다.
두번째는 같이 해요 세번째 유저가 이 1번에 쓴 답글이다.
그래서 포린키는 board테이블에 있으면 안되고 Repyl테이블에 boardId여야한다.
이 reply는 연관관계의 주인이 아니기 떄문에
Fk가 아니기떄문에 데이터베이스에 컬럼을 만들지 마세요
나는 그냥 Board를 셀렉트 할때 join문을 값을 얻기 위해 필요한 겁니다 라는 거다!
@OneToMany(mappedBy = "board")
mappedBy뒤에 있는 부분은 무엇인가? 무슨 필드의 이름이라면?
Reply클래스에 있는 boardId가 아니라 board를 적으면 된다.
ManyToOne의 기본전략이 무엇인가?
내가 BOard테이블을 셀렉트하면 id, title, content, count값을 당연히 들고오는데
user값은 FK니까 이걸 들고올지 안들고 올지 결정을 해야하는데
ManyToOne이라고 하면 이 데이터는 한건밖에 없다는 것이니까
기본 전략은 fetch 타입이 Eager전략이다.
이게 무슨말인가하면
너가 Board테이블을 셀렉트를 하면 User정보는 가져간다. 한건밖에 없으니까 바로 조인해서 가져온다.
OneToMany는 페치 타입이 기본전략이 Eager이 아니다.
Board테이블을 셀렉트 할때 user는 한개밖에 안되지만 reply는 엄청많을 수 있기 떄문이다.
그러면 필요하면 들고오고 필요하지 않으면 안들고올게라고 해서 기본 fetch전략이 Lazy전략이다.
옆에 보면 ManyToOne을 보면 기본전략이 아무것도 안적혀 있다.
왜냐하면 기본전략이 Reply를 셀렉트 할때 무조건 들고오라는것이 Eager전략이다.
User를 보면 아무랑도 연관관계가 없다.
Board를 보면
@OneToMany(mappedBy = "board", fetch = FetchType.LAZY) 이렇게 안적어놔도 기본전략이 Lazy인데.
private List<Reply> reply;
생각을 한번 해보자!
내가 상세보기 화면에서 Board를 셀렉트 할때 저 데이터가 필요할떄 그떄 무조건
무엇이 필요한가? 작성자 정보가 필요하니까 user정보가 필요하고 이거는 페치 Eager로 들고와야한다.
BOard들고 올때는 user을 무조건 들고와야한다.
그리고 reply는 무조건 들고와야하는가? 무조건 들고와야한다. 상세 정보 할떄 이 데이터가 필요하기 떄문이다.
만약에 우리가 상세보기할떄 이 댓글이 보이는것이 아니라 UI가 작성자 : Love
제목 : 머시기
내용 : 머시기
댓글이라고 해서 펼치기라는 버튼이 하나 있다고 하면
상세 보기들어갈때 user오브젝트가 필요하고 board오브젝트 필요하고
댓글은 필요하지 않다. 펼치기 버튼을 클릭하기 전에는 댓글이 필요없기 떄문에 Eager전략이 필요없고 Lazy전략을 쓰면 된다. 왜냐하면 필요할때 댕겨오면 되니까!
그런데 우리는 UI를 이런식으로 만든다고 하면
BOard를 셀렉트 할때 user와 reply 정보를 다들고와야하기 떄문에 Eager전략으로 바꿔줘야한다.
package com.cos.blog.model;
import java.sql.Timestamp;
import java.util.List;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
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")
private int count; // 조회수
@ManyToOne(fetch = FetchType.EAGER) // Many = Board , User = One
@JoinColumn(name="userId")
private User user; // DB는 오브젝트를 저장할 수 없다. 자바는 오브젝트를 저장할 수 있다.
@OneToMany(mappedBy = "board", fetch = FetchType.EAGER) //mappedBy는 연관관계의 주인이 아니다(난 FK가 아니다) 데이터베이스에 컬럼을 만들지말라
private List<Reply> reply;
@CreationTimestamp
private Timestamp createDate;
}
'Back-End > 블로그 만들기 With SpringBoot' 카테고리의 다른 글
SpringBoot Blog만들기 회원가입을 위한 insert 테스트 (0) | 2023.02.23 |
---|---|
SpringBoot blog 만들기 Json 사용법 (1) | 2023.02.16 |
SpringBoot Blog 만들기 Reply 테이블 생성 (0) | 2023.02.15 |
SpringBoot blog 만들기 Board 테이블 생성 (0) | 2023.02.15 |
SpringBoot blog 만들기 User 테이블 생성 (0) | 2023.02.15 |