본문 바로가기
Spring/JPA

N대N 연관관계 설정 2 (중간테이블을 만들어서 사용하는 방법)

by step 1 2021. 8. 29.
반응형

이전에 작성한 내용을 중간테이블을 만들어서 사용하는 방법

N대1로 변경하여 작성하였다.

 

BookAndAuthor 클래스 작성

package com.example.bookmanager.domain;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;

import javax.persistence.*;

@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;
}

 

기존 코드 수정

Author 클래스 수정

package com.example.bookmanager.domain;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Entity
@NoArgsConstructor
@Data
@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;

    // N대N 관계 설정
    // stack 오버 플로우 처리
    // null 예외처리 방지 설정
//    @ManyToMany
    @OneToMany
    @JoinColumn(name = "author_id")
    @ToString.Exclude
    private List<BookAndAuthor> bookAndAuthor = new ArrayList<>();

    // ... => 배열로 받는다는 의미
    // Book 정보가 여러개 존재하면 모두 처리
    public void addBookAndAuthors(BookAndAuthor... bookAndAuthors){
        Collections.addAll(this.bookAndAuthor, bookAndAuthors);
//        if (book != null){
//            for (Book book1 : book){
//                this.books.add(book1);
//            }
//        }
    }
}

 

Book 클래스 수정

package com.example.bookmanager.domain;

import com.example.bookmanager.domain.listener.Auditable;
import com.example.bookmanager.domain.listener.MyEntityListener;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Entity
@NoArgsConstructor
@Data
// 상속받은 BaseEntity를 사용하기 위해서 ToString, EqualsAndHashCode 재정의
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
// 해당 객체에 사용자가 정의한 Entity Listener가 수행되도록 선언해준다.
public class Book extends BaseEntity {
    @Id
    // 테이블마다 ID값이 증가하도록 설정
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String category;

    private Long authorId;

//    private Long publisherId;

    @OneToOne(mappedBy = "book")
    // toString에서 제외, 설정하지 않으면 스택오버플로우 에러가 발생
    @ToString.Exclude
    private BookReviewInfo bookReviewInfo;

    @OneToMany
    @JoinColumn
    @ToString.Exclude
    private List<Review> reviews = new ArrayList<>();

    @ManyToOne
    @ToString.Exclude
    private Publisher publisher;

    // N대N 관계 설정
    // stack 오버 플로우 처리
    // null 예외처리 방지 설정
//    @ManyToMany
    @OneToMany
    @JoinColumn(name = "book_id")
    @ToString.Exclude
    private List<BookAndAuthor> bookAndAuthor = new ArrayList<>();

    // ... => 배열로 받는다는 의미
    // Author 정보가 여러개 존재하면 모두 처리
    public void addBookAndAuthor(BookAndAuthor... bookAndAuthors) {
        Collections.addAll(this.bookAndAuthor, bookAndAuthors);
    }

//    private String author;

//    private LocalDateTime createdAt;
//
//    private LocalDateTime updatedAt;
//
//    //@PrePersist
//    public void prePersist(){
//        this.createdAt = LocalDateTime.now();
//        this.updatedAt = LocalDateTime.now();
//    }
//
//    //@PreUpdate
//    public void preUpdate(){
//        this.updatedAt = LocalDateTime.now();
//    }
}

 

BookAndAuthorRepository 인터페이스 생성 (JPA 이용)

package com.example.bookmanager.repository;

import com.example.bookmanager.domain.BookAndAuthor;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BookAndAuthorRepository extends JpaRepository<BookAndAuthor, Long> {

}

 

Test 코드 수정

package com.example.bookmanager.repository;

import com.example.bookmanager.domain.Author;
import com.example.bookmanager.domain.Book;
import com.example.bookmanager.domain.BookAndAuthor;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.transaction.Transactional;

import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest
class AuthorRepositoryTest {

    @Autowired
    private AuthorRepository authorRepository;
    @Autowired
    private BookRepository bookRepository;
    @Autowired
    private BookAndAuthorRepository bookAndAuthorRepository;

    @Test
    @Transactional
        // LazyInitializationException 에러 처리를 위해서 설정
    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.addBookAndAuthor(bookAndAuthor1);
        book2.addBookAndAuthor(bookAndAuthor2);
        book3.addBookAndAuthor(bookAndAuthor3, bookAndAuthor4);
        book4.addBookAndAuthor(bookAndAuthor5, bookAndAuthor6);

        author1.addBookAndAuthors(bookAndAuthor1, bookAndAuthor3, bookAndAuthor5);
        author2.addBookAndAuthors(bookAndAuthor2, bookAndAuthor4, bookAndAuthor6);
//        book1.addAuthor(author1);
//        book2.addAuthor(author2);

        // 공동저자로 설정
//        book3.addAuthor(author1,author2);
//        book4.addAuthor(author1,author2);
//        book3.setAuthors(Lists.newArrayList(author1, author2));
//        book4.setAuthors(Lists.newArrayList(author1, author2));

//        author1.addBook(book1, book3, book4);
//        author2.addBook(book2, book3, book4);
//        author1.setBooks(Lists.newArrayList(book1, book3, book4));
//        author2.setBooks(Lists.newArrayList(book2, book3, book4));
        // 연관 관계 설정 종료

        bookRepository.saveAll(Lists.newArrayList(book1,book2,book3,book4));
        authorRepository.saveAll(Lists.newArrayList(author1, author2));

        bookRepository.findAll().get(2).getBookAndAuthor().forEach(o -> System.out.println("3번째 책 가져오기: " + o.getAuthor()));
        authorRepository.findAll().get(0).getBookAndAuthor().forEach(o -> System.out.println("martin이 쓴 책 출력: " + o.getBook()));
//        System.out.println("3번째 책 가져오기: " + bookRepository.findAll().get(2).getAuthors());
//        System.out.println("martin이 쓴 책 출력: " + authorRepository.findAll().get(0).getBooks());
    }


    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);
    }
}

 

콘솔창 확인

3번째 책 가져오기: Author(super=BaseEntity(createdAt=2021-08-29T16:46:49.093, updatedAt=2021-08-29T16:46:49.093), id=1, name=martin, country=null)
3번째 책 가져오기: Author(super=BaseEntity(createdAt=2021-08-29T16:46:49.103, updatedAt=2021-08-29T16:46:49.103), id=2, name=steve, country=null)

martin이 쓴 책 출력: Book(super=BaseEntity(createdAt=2021-08-29T16:46:49.018, updatedAt=2021-08-29T16:46:49.018), id=1, name=책 1, category=null, authorId=null)
martin이 쓴 책 출력: Book(super=BaseEntity(createdAt=2021-08-29T16:46:49.076, updatedAt=2021-08-29T16:46:49.076), id=3, name=개발책1, category=null, authorId=null)
martin이 쓴 책 출력: Book(super=BaseEntity(createdAt=2021-08-29T16:46:49.080, updatedAt=2021-08-29T16:46:49.080), id=4, name=개발책2, category=null, authorId=null)
   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)
    )
반응형

'Spring > JPA' 카테고리의 다른 글

영속성 구성을 위한 mysql 설정  (0) 2021.09.12
N 대 N 연관관계 설정  (0) 2021.08.29
1대N 연관관계  (0) 2021.08.06
1 대 1 관계 설정하는 방법  (0) 2021.07.24
연관 관계 설정 -1  (0) 2021.07.24