일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 그리디
- springdatajpa
- AOP
- Servlet
- 김영한
- Thymeleaf
- Spring Boot
- 스프링 핵심 원리
- kotlin
- spring
- SpringBoot
- 자바
- Proxy
- Android
- QueryDSL
- 알고리즘
- http
- java
- 인프런
- jpa
- Exception
- 스프링 핵심 기능
- 스프링
- Greedy
- db
- pointcut
- 백준
- transaction
- JPQL
- JDBC
- Today
- Total
개발자되기 프로젝트
M : N(다대다) 연관관계 - 2 본문
사실 ManyToMany관계를 직접적으로 많이 사용하지는 않는다고 한다.
그리고 ManyToMany는 내가 만들지 않은 중간 테이블을 생성하게 된다.
앞의 글에서 Book과 Author은 many to many 관계인데, 중간에 BookAndAuthor가 있다고 해보자.
즉, Book : BookAndAuthor , BookAndAuthor : Author의 관계이다.
BookAndAuthor입장에서 정리하면 아래처럼 나타낼 수 있다.
BookAndAuthor : Book = N : 1
BookAndAuthor : Author = N : 1
즉, 1개의 many to many 관계가 아니라, 2개의 many to one 관계로 변경이 가능하다.
1. BookAndAuthor
- BookAndAuthor : Book = N : 1
- BookAndAuthor : Author = N : 1
@Entity
@NoArgsConstructor
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class BookAndAuthor extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
@ManyToOne
private Book book;
@ManyToOne
private Author author;
}
* Repository
public interface BookAndAuthorRepository extends JpaRepository<BookAndAuthor, Long> {
}
2. Author
- Author : BookAndAuthor = 1 : N --> @OneToMany, @JoinColumn(name = "one_PK")
@Entity
@Data
@NoArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class Author extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long Id;
private String name;
private String country;
//@ManyToMany
@OneToMany
@JoinColumn(name = "author_id")
@ToString.Exclude
private List<BookAndAuthor> bookAndAuthors = new ArrayList<>();
public void addBookAndAuthors(BookAndAuthor ... bookAndAuthors){ //배열로 받겠다.
Collections.addAll(this.bookAndAuthors, bookAndAuthors); //book 정보가 여러개 들어오면 한꺼번에 저장.
}
}
그리고 addBookAuthor method에서 BookAndAuthor ... bookAndauthors는 배열로 받는다는 의미임.
Colllection : 데이터의 집합 그룹
- List : LinkedList, Stack, Vector, ArrayList
- Set : HashSet, StoredSet
Collections Class :
This class consists exclusively of static methods that operate on or return collections. It contains polymorphic algorithms that operate on collections, "wrappers", which return a new collection backed by a specified collection, and a few other odds and ends.
Collections.addAll() :
Adds all of the specified elements to the specified collection. Elements to be added may be specified individually or as an array.
3.Book
- Book : BookAndAuthor = 1 : N --> @OneToMany, @JoinColumn(name = "one_PK")
@Entity
@NoArgsConstructor
@Data
@EntityListeners(value = AuditingEntityListener.class)
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class Book extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Long authorId;
private String category;
@OneToOne(mappedBy = "book")
@ToString.Exclude
private BookReviewInfo bookReviewInfo;
@OneToMany
@JoinColumn(name = "book_id") //중간 테이블 만들지 않기 위해, 나의pk를 상대의 fk로
@ToString.Exclude
private List<Review> reviews = new ArrayList<>(); //null point exception 방지
@ManyToOne
@ToString.Exclude
private Publisher publisher;
//@ManyToMany
@OneToMany
@JoinColumn(name = "book_id")
@ToString.Exclude
private List<BookAndAuthor> bookAndAuthors = new ArrayList<>(); //NPE방지하기위해 array생성
public void addBookAndAuthors(BookAndAuthor ... bookAndAuthors){ //배열로 받겠다.
Collections.addAll(this.bookAndAuthors, bookAndAuthors); //book 정보가 여러개 들어오면 한꺼번에 저장.
}
}
4. Test
쪼금.. 해줘야 할 게 많다. 핵심은 Book, Author, BookAndAuthro를 생성하고
Book 와 BookAndAuthor의 관계 맺기, Author와 BookAndAuthor의 관계 맺기 이다.
@SpringBootTest
class AuthorRepositoryTest {
@Autowired
private AuthorRepository authorRepository;
@Autowired
private BookRepository bookRepository;
@Autowired
private BookAndAuthorRepository bookAndAuthorRepository;
@Test
@Transactional
void manyToManyTest(){
Book book1 = givenBook("책1");
Book book2 = givenBook("책2");
Book book3 = givenBook("개발책1");
Book book4 = givenBook("개발책2");
Author author1 = givenAuthor("martin");
Author author2 = givenAuthor("steve");
BookAndAuthor bookAndAuthor1 = givenBookAndAuthor(book1, author1);
BookAndAuthor bookAndAuthor2 = givenBookAndAuthor(book2, author2);
BookAndAuthor bookAndAuthor3 = givenBookAndAuthor(book3, author1);
BookAndAuthor bookAndAuthor4 = givenBookAndAuthor(book3, author2);
BookAndAuthor bookAndAuthor5 = givenBookAndAuthor(book4, author1);
BookAndAuthor bookAndAuthor6 = givenBookAndAuthor(book4, author2);
book1.addBookAndAuthors(bookAndAuthor1);
book2.addBookAndAuthors(bookAndAuthor2);
book3.addBookAndAuthors(bookAndAuthor3, bookAndAuthor4);
book4.addBookAndAuthors(bookAndAuthor5, bookAndAuthor6);
author1.addBookAndAuthors(bookAndAuthor1, bookAndAuthor3, bookAndAuthor5);
author2.addBookAndAuthors(bookAndAuthor2, bookAndAuthor4, bookAndAuthor6);
//여기까지 연관관계 다 맺음
bookRepository.saveAll(Lists.newArrayList(book1, book2, book3, book4)); //save all은 lists를 받아서 다저장해줌
authorRepository.saveAll(Lists.newArrayList(author1, author2));
bookRepository.findAll().get(2).getBookAndAuthors().forEach(o -> System.out.println(o.getAuthor()));
authorRepository.findAll().get(0).getBookAndAuthors().forEach(o -> System.out.println(o.getBook()));
}
private Book givenBook(String name){
Book book = new Book();
book.setName(name);
return bookRepository.save(book);
}
private Author givenAuthor(String name){
Author author = new Author();
author.setName(name);
return authorRepository.save(author);
}
private BookAndAuthor givenBookAndAuthor(Book book, Author author){
BookAndAuthor bookAndAuthor = new BookAndAuthor();
bookAndAuthor.setBook(book);
bookAndAuthor.setAuthor(author);
return bookAndAuthorRepository.save(bookAndAuthor);
}
}
5. 결과
의도한 대로 결과가 잘 나왔다.
DDL을 봐도 이전에 생겼던 중간 table이 없어지고 직접 만들어준 book_and_author가 생성되었다.
직접 만들어 줬기 때문에, created_at, updated_at이 있는걸 볼 수 있다.
Hibernate:
create table author (
id bigint generated by default as identity,
created_at timestamp,
updated_at timestamp,
country varchar(255),
name varchar(255),
primary key (id)
)
Hibernate:
create table book (
id bigint generated by default as identity,
created_at timestamp,
updated_at timestamp,
author_id bigint,
category varchar(255),
name varchar(255),
publisher_id bigint,
primary key (id)
)
Hibernate:
create table book_and_author (
id bigint generated by default as identity,
created_at timestamp,
updated_at timestamp,
author_id bigint,
book_id bigint,
primary key (id)
)
이처럼 many to many 를 그대로 쓸 수 있지만 중간 table을 직접 만들어 줘서
자주쓰는 many to one의 관계로 변경이 가능하다.
'JPA' 카테고리의 다른 글
Transaction, @Transactional (0) | 2021.07.02 |
---|---|
JPA/Hibernate 초기화(ddl-auto, initialization-mode 등) (0) | 2021.07.01 |
M : N(다대다) 연관관계 - 1 (0) | 2021.06.22 |
N : 1 연관관계 #2 (0) | 2021.06.21 |
N : 1 연관관계 - @ManyToOne (0) | 2021.06.21 |