220719_TIL(2) : 연관관계

백승한·2022년 7월 19일
0

연관관계

1:1 연관관계

출판사에서는 여러 개의 책을 만들 수는 있지만,
똑같은 책을 여러 출판사에서 나눠서 출판하는 경우는 없기 때문에 책과 출판사는 N(책):1(출판사)이다.

작가와 책의 연관관계는 하나의 책을 만드는데 여러 작가들이 함께 작업 할 수도 있고,
한 작가가 여러 개의 책을 만들수도 있기 때문에 N:N

사실 현업에서는 의외로 1:1 연관관계가 많이 있다.

  • 리뷰 평점과 리뷰갯수의 null값을 허용하는지 마는지에 따라 자료형을
    Wrapper 타입 / Primitive type (0 가능)으로 나뉘어진다.
@ToString(callSuper = true) // 상속받는 super 클래스의 정보를 toString에 포함하도록 설정
@EqualsAndHashCode(callSuper = true) // 

call next value for hibernate_sequence
매번 생성할 때 하이버네이트 시퀀스를 사용하는데, 해당 시퀀스를 공용으로 사용해서 따로따로 증가가 아니라 이어서 증가한다.

아이디 자동생성 전략을 IDENTITY로 바꾸니까 hibernate_sequence을 찾지 못한다는 에러가 나온다.

@OneToOne(optional = false)
private Book book; // 테이블을 생성시 book_id **not null** / outer join에서 inner join으로 된다.

BookReviewInfo 엔티티에서 mappedBy = "bookReviewInfo"를 설정해주니까
BookReviewInfo 테이블에서 book_id 칼럼이 사라졌다.
즉 mappedBy 속성을 설정하게 되면 연관 키(book_id)를 해당 테이블에서는 더 이상 갖지않게 된다.

반대로 Book 엔티티에서 mappedBy 속성을 설정해보자. (BookReviewInfo에서 속성을 삭제)

book 테이블에서 book_review_info 칼럼이 사라지고, BookReviewInfo 테이블에서는 book_id속성이 존재한다.

1:N 연관관계

findAll()은 where 조건이 존재하지 않는다.

@OneToMany(fetch = FetchType.EAGER)  // 널포인트 인셉션을 방지 하기 위해서
@JoinColumn(name = "user_id", insertable = false, updatable = false)
@ToString.Exclude
// User쪽에서 UserHistory를 저장 및 수정을 금지시켜놓는 설정
private List<UserHistory> userHistories = new ArrayList<>(); // NUllpointExceoption 방지하기 위해 기본 리스트 설정
// JPA가 조회할 때 값이 조회하지 않으면 빈 리스트를 넣어준다. 
// 그래서 문제가 일반적으로는 없지만, JPA PERSIST를 하기 전에 해당 값이 NuLL이면 로직에 따라서 예외가 발생한다.

@JoinColumn : 엔티티가 어떤 컬럼으로 join으로 할지 지정해주는 어노테이션

@JoinColumn의 디폴트 설정은 해당 필드의 name을 활용하기 때문에, user_history 테이블에서 userHistories 변수를 활용해서 user_histories_id 컬럼이 생겼다.
이렇게 되면 안된다. 문제 해결하기 위해서 @JoinColumn(name = 'user_id") 설정해준다.

User 엔티티는 userHistories 변수를 추가하거나 수정하면 안되는 값이다. 즉 조회 전용(readonly) 값이다. 그렇게 하기 위한 설정이 @JoinColumn(insertable = false, updatable = false)!
이렇게 하면 User 엔티티 입장에서는 userHistories를 저장하지도 못하도 수정하지도 못한다.

@OneToMany방향에서 @JoinColumn 지정을 안해주면 중간 테이블이 생긴다.
@OneToMany방향으로 갖고있는 엔티티는 DB상으로는 FK값을 갖고있지 않다.

N:1 연관관계

JPA에서는 연관된 객체를 FK를 통해서 조회하는 것이 아니라 Getter를 통해서 가져온다.
그래서 @OneToMany를 사용해야 할지, @ManyToOne을 사용해야 할지 혹은 둘다 사용해서 양방향으로 관계를 걸어야할지를 결정하는 조건은 어느 Entity에서 연관 엔티티가 필요한지를 생각해보면 알 수 있다.

지금 같은 경우에는, UserHistory 값을 조회해서 User객체를 조회해 볼 케이스는 많이 없을 것이다. 반대로, User객체에서 변경이력들을 보기 위해서 GetUserHisotry를 조회하는 경우는 많이 생길 것이다. 결과적으로 User객체어서 @OneToMany를 활용해서 UserHistory에 연관관계를 맺어주는 것이 나은 방법이다.

N:N 연관관계

@ManyToMany
private List<Author> authors = new ArrayList<>();
@ManyToMany
private List<Book> books = new ArrayList<>();

서로 @ManyToMany 설정 -> 중간 테이블이 생성된다.
@OneToMany를 했을 때도 같은 상황이 나타났고, 그 때는 @JoinColumn을 통해서 중간 테이블을 제거하고 직접 참조할 수 있도록 했다.

N:N 관계는 데이터베이스에서는 물론이고, 엔티티에서도 거의 사용하지 않는다. 중간 엔티티 하나를 추가해서 N:N 관계를 1:N / N:1 매핑으로 풀어내서 사용하자.

  • 중간 테이블에 컬럼을 추가할 수 없고,
    세밀하게 쿼리를 실행하기 어렵기 때문에 실무에서 사용하기에는 한계가 있다.

포인트

설계단계에서는 가급적이면 양방향을 쓰지말고, 단방향으로 설계해야한다.
양방향 연관관계일 경우, 연관관계의 주인을 정해야 한다. 주로 외래키가 있는 쪽으로 정한다.
1:N일 경우, N (Many)쪽에 외래키가 존재하게된다.
Order에 있는 Member가 주인이 되고, Member에 있는 orders는 mapped by로 연관관계 거울이 된다(단순 읽기위해).
연관관계 주인쪽에 값을 셋팅해야 값이 변경된다. 거울쪽은 단순하게 조회용으로만 쓰인다.

외래키가 가까운 곳에 있는 거를 연관관계의 주인을 잡는다. 그래야 모든게 편하다

profile
방문해주셔서 감사합니다🙂

0개의 댓글