Jpa 연관관계 살펴보기 (N:N)
다대다 관계에 있는 Entity의 설정 과정을 살펴보도록 하겠습니다. 중간 테이블을 자동으로 생성하는 @ManyToMany 방식보다는 매핑 테이블을 별도로 만들어서 각각의 엔티티와 1:N N:1 관계를 맺도록 해보도록 하겠습니다.
ERD
이전 시간에 살펴본 ERD에서 다대다 연결 관계를 가지고 있는 book과 author를 통해 살펴보도록 하겠습니다.
우선 book과 author 중간 매핑 테이블을 만들어줄 Entity를 선언해줍니다.
BookAndAuthor
@Entity
@NoArgsConstructor
@Data
public class BookAndAuthor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
private Book book;
@ManyToOne
private Author author;
}
BookAndAuthor Entity의 경우 Book, Author Entity와 1:N 관계를 맺고 있으므로 @ManyToOne 어노테이션을 선언해줍니다.
이에 따라 Book과 Author Entity에도 BookAndAuthor Entity 관련 컬럼을 선언해줍니다.
Book
@Data
@NoArgsConstructor
@Entity
public class Book {
...
@OneToMany
@JoinColumn(name = "book_id")
@ToString.Exclude
private List<BookAndAuthor> bookAndAuthors = new ArrayList<>();
public void addBookAndAuthor(BookAndAuthor... bookAndAuthors){
Collections.addAll(this.bookAndAuthors,bookAndAuthors);
}
}
Book과 BookAndAuthor의 관계는 1:N이므로 @OneToMany 어노테이션을 선언해주고, List로 선언해줍니다.
이미 매핑 테이블을 선언해두었기 때문에 새로운 테이블이 필요하지 않으므로 @JoinColumn을 join될 Entity의 어떤 컬럼에 매핑된다는 것을 지정해줍니다.
아래 addBookAndAuthor는 bookAndAuthor 값을 세팅해주는 메서드입니다.
Author
@Entity
@NoArgsConstructor
@Data
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String country;
@OneToMany
@JoinColumn(name = "author_id")
@ToString.Exclude
private List<BookAndAuthor> bookAndAuthors = new ArrayList<>();
public void addBookAndAuthor(BookAndAuthor... bookAndAuthors){
Collections.addAll(this.bookAndAuthors,bookAndAuthors);
}
}
Author와 BookAndAuthor의 관계도 1:N이므로 @OneToMany 어노테이션을 선언해주고, List로 선언해줍니다. 또한 @JoinColumn도 선언해주었습니다.
아래 addBookAndAuthor는 bookAndAuthor 값을 세팅해주는 메서드입니다.
위와 같이 Entity를 선언할 경우 나오는 ddl은 다음과 같습니다.
Hibernate:
create table author (
id bigint generated by default as identity,
country varchar(255),
name varchar(255),
primary key (id)
)
Hibernate:
create table book (
id bigint generated by default as identity,
category varchar(255),
name varchar(255),
primary key (id)
)
Hibernate:
create table book_and_author (
id bigint generated by default as identity,
author_id bigint,
book_id bigint,
primary key (id)
)
book과 author에서는 book_and_author에 대한 FK가 존재하지 않고 book_and_author에만 book, author에 대한 FK가 생성된 것을 알 수 있습니다.
예제 )
public BookAndAuthor givenBookAndAuthor(Book book, Author author){
BookAndAuthor bookAndAuthor = new BookAndAuthor();
bookAndAuthor.setBook(book);
bookAndAuthor.setAuthor(author);
return bookAndAuthorRepository.save(bookAndAuthor);
}
@Transactional
@Test
public void test3(){
// Book 생성
Book book1 = new Book();
book1.setName("book1");
book1.setCategory("category1");
Book book2 = new Book();
book2.setName("book2");
book2.setCategory("category2");
bookRepository.save(book1);
bookRepository.save(book2);
// Author 생성
Author author1 = new Author();
author1.setName("author1");
author1.setCountry("country1");
Author author2 = new Author();
author2.setName("author1");
author2.setCountry("country1");
authorRepository.save(author1);
authorRepository.save(author2);
// BookAndAuthor 생성
BookAndAuthor bookAndAuthor1 = givenBookAndAuthor(book1,author1);
BookAndAuthor bookAndAuthor2 = givenBookAndAuthor(book1,author2);
BookAndAuthor bookAndAuthor3 = givenBookAndAuthor(book2,author1);
BookAndAuthor bookAndAuthor4 = givenBookAndAuthor(book2,author2);
// book, author에도 연관 관계 추가
book1.addBookAndAuthor(bookAndAuthor1,bookAndAuthor2);
book2.addBookAndAuthor(bookAndAuthor3,bookAndAuthor4);
author1.addBookAndAuthor(bookAndAuthor1,bookAndAuthor2);
author2.addBookAndAuthor(bookAndAuthor3,bookAndAuthor4);
bookRepository.saveAll(Lists.newArrayList(book1,book2));
authorRepository.saveAll(Lists.newArrayList(author1,author2));
bookRepository.findAll().get(1).getBookAndAuthors().forEach(System.out::println);
}
출력
Hibernate:
select
book0_.id as id1_1_,
book0_.category as category2_1_,
book0_.name as name3_1_
from
book book0_
BookAndAuthor(id=3, book=Book(id=2, name=book2, category=category2), author=Author(id=1, name=author1, country=country1))
BookAndAuthor(id=4, book=Book(id=2, name=book2, category=category2), author=Author(id=2, name=author1, country=country1))
연관된 모든 값들을 출력해주는 것을 알 수 있습니다.
이번 포스팅까지 Entity relation에 대해 알아보았습니다. 의도하지 않은 중간 테이블이 생기거나, 추가적인 쿼리가 발생하는 것을 막기 위해 많이 시도해보는 것이 좋을 것 같습니다.