Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
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
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

M : N(다대다) 연관관계 - 2 본문

JPA

M : N(다대다) 연관관계 - 2

Seung__ 2021. 6. 22. 23:32

사실 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
Comments