Spring boot: JPA 연관관계 살펴보기 - 1:1

김아무개·2023년 6월 16일
0

Spring Boot 🍃

목록 보기
39/95
post-custom-banner

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이 가능해진다.

profile
Hello velog! 
post-custom-banner

0개의 댓글