연관관계의 주인

myeonji·2022년 2월 15일
0

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

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

< Board >

	// 데이터베이스는 오브젝트(객체)를 저장할 수 없어서 FK를 사용한다. BUT 자바는 객체를 저장할 수 있다. -> 두 개의 충돌!
    // 따라서 자바가 데이터베이스에 맞춰 FK(int)로 저장 -> private int userId;

    // 하지만 JPA를 사용하면! ORM에서는 객체를 그대로 저장할 수 있다. -> FK로 찾는게 아니라 User 객체 바로 넣음
    @ManyToOne // 연관관계 Many = Board, One = User
    @JoinColumn(name = "userId") // 데이터베이스에다가 만들 필드 이름(= 칼럼명) 정할 때 사용
    private User user; // 글 작성한 사람의 id, 자동으로 FK로 만들어지는 것

    // private Reply reply;
    private List<Reply> reply;

Board를 select하려고 하는데, Board안에 User(작성자) 정보 뿐만 아니라 Reply(댓글) 정보도 있다!
따라서 Board안에는 User와 Reply가 연관관계를 맺고 있다.

  • Board - User : @ManyToOne 하나의 작성자는 여러 개의 게시글을 가지고 있다.
  • Board - Reply : @OneToMany 하나의 게시글은 여러 개의 댓글을 가지고 있다. 따라서 Reply는 List형!!

하지만 위의 코드를 보면, Board가 Reply와 @OneToMany 연관관계를 맺는 것을 볼 수 있다.

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

이 부분에서, Joincolumn(name = "replyyId")인 FK는 필요하지 않다.
❓❓❓ 왜냐?!

@Data // getter setter
@NoArgsConstructor // 빈 생성자
@AllArgsConstructor // 전체 생성자
@Builder // 빌더 패턴
@Entity
public class Board {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // auto_increment 사용함 (MySQL)
    private int id;

    @Column(nullable = false, length = 100)
    private String title;

    @Lob // 대용량 데이터
    private String content; // 섬머노트 라이브러리 - 쓴 글이 디자인 되는데 <html> 태그가 섞여서 디자인이 됨 -> 글자수 커짐 -> 대용량

    @ColumnDefault("0") // String이 아니여서 ''로 안 감싸도 됨
    private int count; // 조회수

    // 데이터베이스는 오브젝트(객체)를 저장할 수 없어서 FK를 사용한다. BUT 자바는 객체를 저장할 수 있다. -> 두 개의 충돌!
    // 따라서 자바가 데이터베이스에 맞춰 FK(int)로 저장 -> private int userId;

    // 하지만 JPA를 사용하면! ORM에서는 객체를 그대로 저장할 수 있다. -> FK로 찾는게 아니라 User 객체 바로 넣음
    @ManyToOne // 연관관계 Many = Board, One = User
    @JoinColumn(name = "userId") // 데이터베이스에다가 만들 필드 이름(= 칼럼명) 정할 때 사용
    private User user; // 글 작성한 사람의 id, 자동으로 FK로 만들어지는 것

    @OneToMany
    private List<Reply> reply;

    @CreationTimestamp // 자동으로 현재시간이 들어감
    private Timestamp createDate;
}

Board 테이블을 만든다고 하면, 테이블 컬럼은 아래처럼 생성될 것이다.

  • Id
  • title
  • content
  • userId
  • createDate

그런데 여기서 reply에 @JoinColumn(name = "replyId") 를 설정한다면, replyId가 컬럼에 추가될텐데 이것은 말이 안된다!!! 게시글 하나에 reply는 여러 개인데 그러면 컬럼 replyId 값에는 1, 2, 3, 4, ... 등 여러 개의 댓글 id가 와야한다는 것이다.

💥 따라서 데이터베이스는 1 정규화 -> 원자성 즉, 데이터베이스 하나의 컬럼은 하나의 값을 가져야 한다. 라는 1 정규화가 깨진다.

< 최종 >

	// @JoinColumn(name = "replyId") -> FK 필요없음, 데이터베이스에 컬럼 만들어질 수 없음
    @OneToMany(mappedBy = "board") // mappedBy -> 연관관계의 주인이 아니다. (난 FK가 아니에요!! DB에 칼럼을 만들지 마세요!!)
    private List<Reply> reply;

JoinColumn을 쓰지말고, @OneToMany만 쓴다.
여기서 mappedBy의 의미"연관관계의 주인이 아니다"라는 것이다. 즉, 현재 참조변수(?)인 reply는 FK가 아니라는 것이다.
FK는 Reply 테이블의 boardId가 되어야 한다.

< Board 객체에서 User와 Reply 연관관계 매핑 >

	@ManyToOne(fetch = FetchType.EAGER) // 연관관계 Many = Board, One = User, 기본패치전략 FetchType.EAGER (1건)
    @JoinColumn(name = "userId") // 데이터베이스에다가 만들 필드 이름(= 칼럼명) 정할 때 사용
    private User user; // 글 작성한 사람의 id, 자동으로 FK로 만들어지는 것

    // @JoinColumn(name = "replyId") -> FK 필요없음, 데이터베이스에 컬럼 만들어질 수 없음
    @OneToMany(mappedBy = "board", fetch = FetchType.LAZY) // mappedBy -> 연관관계의 주인이 아니다. (난 FK가 아니에요!! DB에 칼럼을 만들지 마세요!!), 그저 Board를 select할 때 값을 얻기 위해 필요한 것, 기본패치전략 FetchType.LAZY (필요할 때만..)
    private List<Reply> reply;

여기서

  • @ManyToOne의 기본 패치 전략은 EAGER !!! (fetch = FetchType.EAGER)이다. user 데이터가 한 건(1건) 밖에 없다는 것이다. Board 테이블을 select 하면 1건 밖에 없으니 user를 가져오겠다!
  • @OneToMany의 기본 패치 전략은 LAZY !!! (mappedBy = "board", fetch = FetchType.LAZY)이다. Board 테이블을 select 할 때 reply 갯수가 너무 많다..(한 게시글에 댓글 여러개 가능) 따라서 필요할 때만 들고 오겠다!

라는 것이다.

❗❗ 하지만 ❗❗
Board (게시글) 상세 페이지를 생각해보면,
상세페이지에 들어가면 작성자(User)와 댓글(Reply)는 꼭 있어야 한다.
즉, !!! 자동으로 불러와야 한다는 것이다. 이는 EAGER 이다.

@OneToMany의 기본 패치 전략은 LAZY이지만, EAGER로 바꿔줘야 한다.

< Board 객체에서 User와 Reply 연관관계 매핑 변경 >

	@ManyToOne(fetch = FetchType.EAGER) // 연관관계 Many = Board, One = User, 기본패치전략 FetchType.EAGER (1건)
    @JoinColumn(name = "userId") // 데이터베이스에다가 만들 필드 이름(= 칼럼명) 정할 때 사용
    private User user; // 글 작성한 사람의 id, 자동으로 FK로 만들어지는 것

    // @JoinColumn(name = "replyId") -> FK 필요없음, 데이터베이스에 컬럼 만들어질 수 없음
    @OneToMany(mappedBy = "board", fetch = FetchType.EAGER) // mappedBy -> 연관관계의 주인이 아니다. (난 FK가 아니에요!! DB에 칼럼을 만들지 마세요!!), 그저 Board를 select할 때 값을 얻기 위해 필요한 것, 기본패치전략 FetchType.LAZY (필요할 때만..)
    private List<Reply> reply;

0개의 댓글