본문 바로가기
JPA

[JPA] 다대다 연관관계 (단방향)

by 2nyong 2023. 4. 29.

목차


    테이블 설계

    다대다 연관관계를 고객이 주문한 음식(Food)고객(Member)을 예시로 설명하겠습니다.

    • 여러 가지의 음식(Food)은 여러 명의 고객(Member)에게 주문될 수 있다.
    • 음식(Food) : 고객(Member) = N : M

     

    단방향 연관관계를 위한 추가 조건은 다음과 같습니다.

    • Food Entity는 Food.memberList 필드를 통해 음식을 주문한 고객(List<Member)에 접근할 수 있다.
    • Member Entity는 Food Entity에 직접 접근할 수 없다.


    Entity 관계 매핑

    - Food Entity

    • 다대다 단방향 연관관계에서 외래키의 주인입니다.
    @Entity
    public class Food {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
        private double price;
        
        @ManyToMany
        @JoinTable(name = "orders", // 중간 테이블 생성
                joinColumns = @JoinColumn(name = "food_id"),
                inverseJoinColumns = @JoinColumn(name = "member_id"))
        private List<Member> memberList = new ArrayList<>();
    
        public void addMemberList(Member member) {
            this.memberList.add(member);
        }
        
        // constructor, getter, setter, ...
    }

     

    - Member Entity

    @Entity
    public class Member {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
            
        // constructor, getter, setter, ...
    }

     

    • 다대다 단방향 매핑에서는 외래키의 주인에 해당하는 Entity에 반대편 Entity를 Collection 프레임워크로 감싼 형태의 참조 필드로 작성합니다.
    • 참조 필드 위에 @ManyToOne 애노테이션과 @JoinColumn 애노테이션을 추가로 작성합니다.
    • @JoinColumnname 속성은 다대다 관계를 풀어줄 연결 테이블의 이름을 지정합니다.
    • @JoinColumnjoinColumns 속성은 다대다 연관관계에서 외래키의 주인에 해당하는 외래키를 매핑합니다.
    • @JoinColumninverseJoinColumns 속성은 다대다 연관관계에서 외래키의 주인에 해당하지 않는 외래키를 매핑합니다.
    • 위의 Entity 관계를 적용하여 테이블을 생성하였을 때, 실행되는 SQL은  아래와 같습니다.
    CREATE TABLE FOOD (
        ID BIGINT NOT NULL AUTO_INCREMENT,
        NAME VARCHAR(255),
        PRICE FLOAT(53) NOT NULL,
        PRIMARY KET (ID)
    );
    
    CREATE TABLE MEMBER (
        ID BIGINT NOT NULL AUTO_INCREMENT,
        NAME VARCHAR(255),
        PRIMARY KET (ID)
    );
    
    CREATE TABLE ORDERS (
        FOOD_ID BIGINT NOT NULL,
        MEMBER_ID BIGINT NOT NULL
    );
    
    ALTER TABLE ORDERS
        ADD CONSTRAINT FKpktxwhj3x9m4gth5ff6bkqgeb
        FOREIGN KEY (MEMBER_ID)
        REFERENCES MEMBER (ID)
        
    ALTER TABLE ORDERS
        ADD CONSTRAINT FK5g4j2r53ncoltplogbnqlpt30
        FOREIGN KEY (FOOD_ID)
        REFERENCES FOOD (ID)

     

    - @ManyToMany의 한계점

    • @JoinColumn으로 인해 연결 테이블이 Orders 테이블이 생성되었습니다.
    • 자동 생성된 Orders 테이블은 food_id와 member_id만을 컬럼으로 가지고 있는데, 실제 중간 테이블을 활용하기 위해서는 주문 시각이나 주문 번호(order_id)등의 추가 정보가 필요할 수 있습니다.
    • 또, 연결 테이블인 orders 테이블은 숨겨진 테이블이기 때문에 예상할 수 없는 쿼리들이 실행될 수 있습니다.

     

    - @ManyToMany의 한계점 해결 방법

    • 연결용 테이블의 Entity를 만들어 줍니다.
    • @ManyToMany를 @ManyToOne@OneToMany를 활용하여 일대다, 다대일 관계로 풀어줍니다.
    • 위 방식은 여기에서 설명하겠습니다.

    Entity 관계 사용법

    - 저장

    @Transactional
    @SpringBootTest
    public class ManyToManyTest {
    
        @Autowired FoodRepository foodRepository;
        @Autowired MemberRepository memberRepository;
    
        @Test
        @Rollback(value = false)
        @DisplayName("N대M 단방향 저장 테스트")
        void OneWaySaveTest() {
            // 음식(Food)
            Food food = new Food("후라이드 치킨", 15000);
            foodRepository.save(food);
    
            // 고객(Member)1
            Member member1 = new Member("Inyong");
            memberRepository.save(member1);
            food.addMemberList(member1);
    
            // 고객(Member)1
            Member member2 = new Member("Robert");
            memberRepository.save(member2);
            food.addMemberList(member2);
        }
    }

     

    • 위 코드 실행 시, 적용되는 SQL은 다음과 같습니다.
    • 외래 키를 받아 중간 테이블인 Orders에 INSERT SQL이 실행됩니다.
    INSERT INTO FOOD (MEMBER_ID, NAME, PRICE) VALUES (NULL, "후라이드 치킨", 15000);
    INSERT INTO MEMBER (NAME) VALUES ('Inyong');
    INSERT INTO MEMBER (NAME) VALUES ('Robert');
    INSERT INTO ORDERS (FOOD_ID, MEMBER_ID) VALUES (1, 1);
    INSERT INTO ORDERS (FOOD_ID, MEMBER_ID) VALUES (1, 2);

    댓글