ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Jpa 연관관계 살펴보기 (N:N)
    백엔드 2023. 8. 23. 14:39
    728x90
    반응형
    SMALL

    다대다 관계에 있는 Entity의 설정 과정을 살펴보도록 하겠습니다. 중간 테이블을 자동으로 생성하는 @ManyToMany 방식보다는 매핑 테이블을 별도로 만들어서 각각의 엔티티와 1:N N:1 관계를 맺도록 해보도록 하겠습니다.

     

    ERD

    ERD
    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에 대해 알아보았습니다. 의도하지 않은 중간 테이블이 생기거나, 추가적인 쿼리가 발생하는 것을 막기 위해 많이 시도해보는 것이 좋을 것 같습니다.

    반응형
    LIST

    '백엔드' 카테고리의 다른 글

    JPA Transaction  (0) 2023.08.25
    영속성 컨텍스트, Entity Cache, Entity Lifecycle  (2) 2023.08.24
    JPA 연관관계 살펴보기 (1:1, 1:N, N:1)  (4) 2023.08.21
    Entity annotation, Listener  (1) 2023.08.19
    JPA 쿼리메서드  (0) 2023.08.18

    댓글

Designed by Tistory.