• Spring boot: JPA 연관관계 살펴보기 - 1:1
    Spring Boot 🍃 2023. 12. 5. 00:01

    1 대 1 연관관계

    어느 엔티티 쪽에서 상대 엔티티와 반드시 단 하나의 관계를 가지는 것을 말한다.

    1:1 연관관계는 생각보다 실무에서 많이 사용된다.

    실습 1: 단방향

    book 테이블과 bookReviewInfo 테이블이 1:1로 조인하는 경우에 대해 실습해본다.

    bookReviewInfo 엔티티에 book 엔티티를 조인해서 데이터 확인

    엔티티 작성

    Book 엔티티

    @ToString(callSuper = true)
    @Getter @Setter
    @SuperBuilder
    @NoArgsConstructor
    @AllArgsConstructor
    @Entity
    public class Book extends BaseEntity {
    private String name;
    private String category;
    private Long authorId;
    private Long publisherId;
    }

    BookReviewInfo 엔티티

    @ToString(callSuper = true)
    @Getter @Setter
    @SuperBuilder
    @AllArgsConstructor
    @NoArgsConstructor
    @Entity
    public class BookReviewInfo extends BaseEntity {
    @OneToOne(optional = false)
    private Book book;
    private float averageReviewScore;
    private int reviewCount;
    }

    BookReviewInfo 엔티티를 작성할 때 Book 객체를 필드로 작성하고, Book 필드에 연관관계 어노테이션을 붙여주면 JPA가 아래와 같이 해석해준다.

    create table book_review_info (
    average_review_score float(24) not null,
    review_count integer not null,
    book_id bigint not null unique, -- 이 부분이 Book 객체를 해석한 부분
    created_at timestamp(6),
    id bigint generated by default as identity,
    updated_at timestamp(6),
    primary key (id)
    )

    test 코드 실행: 쿼리 확인

    test 코드

    @Test
    void showQuery() {
    // book 데이터 & bookReviewInfo 데이터 생성
    givenBookReviewInfo();
    // bookReviewInfo 조회
    Book result = bookReviewInfoRepository
    .findById(1L)
    .orElseThrow(RuntimeException::new)
    .getBook();
    }

    JPA query

    select
    b1_0.id,
    b1_0.average_review_score,
    b1_0.book_id,
    b2_0.id,
    b2_0.author_id,
    b2_0.category,
    b2_0.created_at,
    b2_0.name,
    b2_0.publisher_id,
    b2_0.updated_at,
    b1_0.created_at,
    b1_0.review_count,
    b1_0.updated_at
    from
    book_review_info b1_0
    join
    book b2_0
    on b2_0.id=b1_0.book_id
    where
    b1_0.id=?

    실습 2: 양방향

    1:1 연관관계를 맺는 경우,

    양방향으로 관계를 설정하는 경우는 극히 드물다.

    양방향 관계는 1:N , N:N의 관계에서 주로 맺게 된다.

    하지만 학습을 위해 실습!


    엔티티 작성

    Book 엔티티 수정

    book 엔티티에도 bookReviewInfo 엔티티 필드를 1:1관계로 추가해준다.

    이떄, 연관관계 어노테이션에 mappedBy="엔티티이름" 설정을 추가해주어, 연관키를 해당 엔티티에서 가지지 않도록 설정을 해주고 optional 설정을 기본값인 true 로 변경해주어야 한다.

    또한, ToString을 사용하는 엔티티라면, 순환참조가 발생하여 StackOverFlow 에러를 만나게 되므로 @ToString.exclude 설정을 주어야 한다.

    public class Book extends BaseEntity {
    @OneToOne(mappedBy = "book")
    @ToString.Exclude
    private BookReviewInfo bookReviewInfo;
    private String name;
    private String category;
    private Long authorId;
    private Long publisherId;
    }

    변환 된 sql 쿼리

    Hibernate:
    create table book (
    author_id bigint,
    created_at timestamp(6),
    id bigint generated by default as identity,
    publisher_id bigint,
    updated_at timestamp(6),
    category varchar(255),
    name varchar(255),
    primary key (id)
    )

    mappedBy 설정으로 인해 create 쿼리에서는 BookReviewInfo_id 필드가 작성되어있지 않다.


    BookReviewInfo 엔티티는 그대로!

    public class BookReviewInfo extends BaseEntity {
    @OneToOne(optional = false)
    private Book book;
    private float averageReviewScore;
    private int reviewCount;
    }

    test 코드 실행: 쿼리 확인

    양방향 확인을 위해 Book 정보를 BookReviewInfoRepository에서 가져오고, BookReviewInfo 정보를 BookRepository에서 가져온다.

    test 코드

    @Test
    void crud() {
    givenBookReviewInfo();
    Book result = bookReviewInfoRepository
    .findById(1L)
    .orElseThrow(RuntimeException::new)
    .getBook();
    System.out.println("[ review id=1 인 book 정보 ]___________🚩 " + result);
    BookReviewInfo result2 = bookRepository
    .findById(1L)
    .orElseThrow(RuntimeException::new)
    .getBookReviewInfo();
    System.out.println("[ book id=1 인 bookReviewInfo 정보 ]___________🚩 " + result2);
    }

    jpa 작성 쿼리!

    Hibernate:
    select
    b1_0.id,
    b1_0.average_review_score,
    b1_0.book_id,
    b2_0.id,
    b2_0.author_id,
    b2_0.category,
    b2_0.created_at,
    b2_0.name,
    b2_0.publisher_id,
    b2_0.updated_at,
    b1_0.created_at,
    b1_0.review_count,
    b1_0.updated_at
    from
    book_review_info b1_0
    join
    book b2_0
    on b2_0.id=b1_0.book_id
    where
    b1_0.id=?
    [ review id=1 인 book 정보 ]___________🚩
    Hibernate:
    select
    b1_0.id,
    b1_0.author_id,
    b2_0.id,
    b2_0.average_review_score,
    b2_0.book_id,
    b2_0.created_at,
    b2_0.review_count,
    b2_0.updated_at,
    b1_0.category,
    b1_0.created_at,
    b1_0.name,
    b1_0.publisher_id,
    b1_0.updated_at
    from
    book b1_0
    left join
    book_review_info b2_0
    on b1_0.id=b2_0.book_id
    where
    b1_0.id=?
    [ book id=1 인 bookReviewInfo 정보 ]___________🚩

    Book 엔티티의 테이블 생성문에서 BookReviewInfo_id 필드가 생성되지 않았지만,

    OneToOne 관계 설정을 해두었기 때문에, 두 엔티티의 공통 필드인 book_id로 Join이 가능해진다.

Designed by Tistory / Custom by 얼거스