어느 엔티티 쪽에서
상대 엔티티와
반드시 단 하나
의 관계를 가지는 것을 말한다.
1:1 연관관계는
생각보다 실무에서 많이 사용된다.
book 테이블과
bookReviewInfo 테이블이 1:1로 조인하는 경우에 대해 실습해본다.
bookReviewInfo 엔티티에 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;
}
@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
void showQuery() {
// book 데이터 & bookReviewInfo 데이터 생성
givenBookReviewInfo();
// bookReviewInfo 조회
Book result = bookReviewInfoRepository
.findById(1L)
.orElseThrow(RuntimeException::new)
.getBook();
}
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=?
1:1 연관관계를 맺는 경우,
양방향으로 관계를 설정하는 경우는 극히 드물다.
양방향 관계는 1:N , N:N의 관계에서 주로 맺게 된다.
하지만 학습을 위해 실습!
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 필드가 작성되어있지 않다.
public class BookReviewInfo extends BaseEntity {
@OneToOne(optional = false)
private Book book;
private float averageReviewScore;
private int reviewCount;
}
양방향 확인을 위해
Book 정보를 BookReviewInfoRepository에서 가져오고,
BookReviewInfo 정보를 BookRepository에서 가져온다.
@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);
}
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이 가능해진다.