@Entity
@NoArgsConstructor
@Data
@ToString(callSuper = true) //상속받은 클래스에 대해 처리해줘야한다. ToString을 재정의 한다.
@EqualsAndHashCode(callSuper = true) //EqualsAndHashCode를 재정의해준다.
//@EntityListeners(value = AuditingEntityListener.class)
public class Book extends BaseEntity {
@Id
@GeneratedValue
private Long id;
private String name;
private String category;
private Long authorId;
private Long publisherId;
@Entity
@NoArgsConstructor
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class BookReviewInfo extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long bookId;
private float averageReviewScore; // 평균
private int reviewCount; // 리뷰 수
}
BaseEntity에 created_at,updated_at 있다는거 있지 말자.
@Test
void crudTest2() {
// 북 하나추가.
Book book = new Book();
book.setName("Jpa 초격차 패키지");
book.setAuthorId(1L);
book.setPublisherId(1L);
bookRepository.save(book);
// 북 목록
System.out.println(">>>> " + bookRepository.findAll());
// 북리뷰정보 출력
BookReviewInfo bookReviewInfo = new BookReviewInfo();
bookReviewInfo.setBookId(1L);
bookReviewInfo.setAverageReviewScore(4.5f);
bookReviewInfo.setReviewCount(2);
bookReviewInfoRepository.save(bookReviewInfo);
// 북리뷰정보 출력
System.out.println(">>> " + bookReviewInfoRepository.findAll());
// 북리뷰정보에 있는 Id가 1인 컬럼의 Id로 book 검색.
Book result = bookRepository.findById(
bookReviewInfoRepository
.findById(1L)
.orElseThrow(RuntimeException::new)
.getBookId()
).orElseThrow(RuntimeException::new);
System.out.println("result >>>> " + result);
}
>>>> [Book(super=BaseEntity(createdAt=2022-02-07T20:42:57.189, updatedAt=2022-02-07T20:42:57.189), id=6, name=Jpa 초격차 패키지, category=null, authorId=1, publisherId=1)]
id가 6으로 시작하는 이유는 H2 DB는 GeneratedValue가 AUTO이면 시퀀스 전략을 가진다.
시퀀스는 id 값을 1을 높이는데 Entity관계 없이 올라간다. 즉 User Entity에서 5명의 유저가 생성되었기 때문에 Book에서는 id값이 6부터 시작하는 것이다.
Entity별 로 따로 설정 해 주려고 한다면 아래와 같이 하자.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
data.sql의 hibernate_sequence는 주석 처리 해 주자.
-- call next value for hibernate_sequence;
자 그럼 잘 된다.
public class BookReviewInfo extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// private Long bookId; 주석처리 하고
@OneToOne //1:1 어노테이션을 걸어주면된다.
private Book book; //Book타입 book을 만들고
Table에는 BookId가 존재하겠지만 Jpa에서는 Entity로 set,get을 하면 릴레이션을 자동으로 연결해준다.
@Test
void crudTest2() {
givenBookReviewInfo(givenBook());
}
private void givenBookReviewInfo(Book book) {
// 북리뷰정보 출력
BookReviewInfo bookReviewInfo = new BookReviewInfo();
// bookReviewInfo.setBookId(1L);
bookReviewInfo.setBook(book);
bookReviewInfo.setAverageReviewScore(4.5f);
bookReviewInfo.setReviewCount(2);
bookReviewInfoRepository.save(bookReviewInfo);
// 북리뷰정보 출력
System.out.println(">>> " + bookReviewInfoRepository.findAll());
// 북리뷰정보에 있는 Id가 1인 컬럼의 Id로 book 검색.
Book result = bookReviewInfoRepository
.findById(1L)
.orElseThrow(RuntimeException::new)
.getBook();
System.out.println("result >>>> " + result);
}
private Book givenBook() {
// 북 하나추가.
Book book = new Book();
book.setName("Jpa 초격차 패키지");
book.setAuthorId(1L);
book.setPublisherId(1L);
// 북 목록
// System.out.println(">>>> " + bookRepository.findAll());
return bookRepository.save(book);
}
create table book_review_info (
id bigint generated by default as identity,
created_at timestamp,
updated_at timestamp,
average_review_score float not null,
book_id bigint,
review_count integer not null,
primary key (id)
)
그대로 book_id bigint, 속성을 추가하는 걸 볼수있다.
Test
...
...
// 북리뷰정보에 있는 Id가 1인 컬럼의 Id로 book 검색.
Book result = bookReviewInfoRepository
.findById(1L)
.orElseThrow(RuntimeException::new)
.getBook();
System.out.println("result >>>> " + result);
...
...
select
bookreview0_.id as id1_2_0_,
bookreview0_.created_at as created_2_2_0_,
bookreview0_.updated_at as updated_3_2_0_,
bookreview0_.average_review_score as average_4_2_0_,
bookreview0_.book_id as book_id6_2_0_,
bookreview0_.review_count as review_c5_2_0_,
book1_.id as id1_1_1_,
book1_.created_at as created_2_1_1_,
book1_.updated_at as updated_3_1_1_,
book1_.author_id as author_i4_1_1_,
book1_.category as category5_1_1_,
book1_.name as name6_1_1_,
book1_.publisher_id as publishe7_1_1_
from
book_review_info bookreview0_
left outer join
book book1_
on bookreview0_.book_id=book1_.id
where
bookreview0_.id=?
😮😮 left outer join을 쓰는걸 볼 수 있다.!!!!!
알아서 join을 해주다니!!!!!!!!!!!!!!!!!!!!!!!! 😮😮
@OneToOne(optional = false)
private Book book;
추가로 optional = false 해주면 Book은 절때로 null을 허용하지 않겠다는 뜻이다.
from
book_review_info bookreview0_
inner join
book book1_
on bookreview0_.book_id=book1_.id
where
bookreview0_.id=?
그럼 left outer join 에서 inner join 으로 바뀌는걸 볼 수 있다.
자 그럼 BookReviewInfo에서 Book정보를 받아 올 수 있게 되었다.
먼저 그냥 Book에 @OneToOne 넣어보자
@OneToOne
private BookReviewInfo bookReviewInfo;
그럼
from
book book0_
left outer join
book_review_info bookreview1_
on book0_.book_review_info_id=bookreview1_.id
left outer join
book book2_
on bookreview1_.book_id=book2_.id
where
book0_.id=?
left outer join을 두번하는걸 볼 수 있다.
이걸 해결하기위해 mappedBy 옵션을 사용할수있다.
mappedBy : (선택사항) 관계를 소유하는 필드입니다. 이 요소는 연결의 역방향(비소유) 측에만 지정됩니다.
@OneToOne(mappedBy = "book")
@ToString.Exclude // ToString 순환참조 걸린다. 릴레이션은 단방향으로 걸고 ToString은 제외해야한다.
private BookReviewInfo bookReviewInfo;
이렇게 하면 DDL 생성도 안하고, Book 과 BookReviewInfo의 1대1 관계가 형성된다.