인프런 커뮤니티 질문&답변

이혜정님의 프로필 이미지
이혜정

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

두 테이블에 동일한 PK를 복합키로 사용했을때 JPA 연관관계 매핑

해결된 질문

작성

·

1.6K

·

수정됨

0

table : prodPlan
pk : factoryCode, prodplanId

column : itemCode ...

table : Item

pk : factoryCode, materialCode

column : itemName...

 

테이블 구조는 이렇고 저는 MSSQL을 연동해서 기본적인 테이블 생성은 MSSQL에서 생성하여 사용하고 있습니다.

 

prodPlan(N) : item(1) 로 ManyToOne 매핑을 하려고 하는데요

 


@Entity
@IdClass(ProdPlanId.class)
public class ProdPlan {
    @Id
    private String factoryCode;
    @Id
    private Long prodplanId;
    @Column(name = "ITEM_CODE")
    private String itemCode;
//...

    @ManyToOne
    @JoinColumn(name = "FACTORY_CODE",referencedColumnName = "FACTORY_CODE",insertable = false, updatable = false)
    @JoinColumn(name = "ITEM_CODE",referencedColumnName = "MATERIAL_CODE",insertable = false, updatable = false)
    private Item item;
}

@Entity
@IdClass(ItemId.class)
public class Item {
    @Id
    private String factoryCode;
    @Id
    private String materialCode;
//...
}

@JoinColumn에 name 속성이 외래키의 이름을 정의하는거라고 알고 있습니다.

 

  1. factoryCode는 외래키가 아니고 두 테이블에서 복합키이자 기본키입니다.

    (복합키는 class ProdPlanId implements Serializable을 사용하여 @IdClass 어노테이션을 사용하였습니다.)

    그래서 위에 코드와 같이 구현을 하면 FACTORY_CODE가 중복컬럼에러가 나서 일단 insertable, updatable = false로 임시해결을 했는데 해결을 한게 아닌 거 같아서 다른 방법이 있나 찾아보던 중 inheritancetype.table_per_class 이 구조가 조금 비슷해보여서 진행하려 했으나 @JoinColumn에서 어떻게 사용해야 하는지 모르겠습니다. (factoryCode는 외래키가 아닙니다! 찾아보니 일대일 매핑만 나왔습니다ㅠㅠ)

  2. 읽기전용이 아닌 prodPlan 테이블의 ITEM_CODE와 Item 테이블의 MATERIAL_CODE를 조인하려 했는데요. 그럴려면 @Column itemCode를 지우고 사용해야 중복컬럼에러를 피할 수 있었습니다. @Column itemCode를 선언하고 조인하는 방법은 없을까요?

답변 2

1

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 혜정님

이 경우 좀 특수하게 매핑을 해야하는데요. 다음 코드를 참고해주세요.

package com.example.jpacomposite.entity;

import jakarta.persistence.*;
import lombok.Data;

import java.io.Serializable;

@Data
@Entity
@Table(name = "ITEM")
@IdClass(ItemId.class)
public class Item implements Serializable {

    @Id
    @Column(name = "FACTORY_CODE")
    private String factoryCode;

    @Id
    @Column(name = "MATERIAL_CODE")
    private String materialCode;

    @Column(name = "MATERIAL_NAME")
    private String materialName;

}
package com.example.jpacomposite.entity;

import jakarta.persistence.Embeddable;
import lombok.Data;

import java.io.Serializable;

@Data
@Embeddable
public class ItemId implements Serializable {

    private String factoryCode;
    private String materialCode;

}
package com.example.jpacomposite.entity;

import jakarta.persistence.*;
import lombok.Data;

import java.io.Serializable;

@Data
@Entity
@Table(name = "PRODPLAN")
@IdClass(ProdPlanId.class)
public class ProdPlan implements Serializable {

    @Id
    @Column(name = "FACTORY_CODE")
    private String factoryCode;

    @Id
    @Column(name = "PRODPLAN_ID")
    private String prodPlanId;

    @Column(name = "ITEM_CODE")
    private String itemCode;

    @ManyToOne
    @JoinColumns({
        @JoinColumn(name = "FACTORY_CODE", referencedColumnName = "FACTORY_CODE", insertable = false, updatable = false),
        @JoinColumn(name = "ITEM_CODE", referencedColumnName = "MATERIAL_CODE", insertable = false, updatable = false)
    })
    private Item item;

}
package com.example.jpacomposite.entity;

import jakarta.persistence.Embeddable;
import lombok.Data;

import java.io.Serializable;

@Data
@Embeddable
public class ProdPlanId implements Serializable {

    private String factoryCode;
    private String prodPlanId;

}
package com.example.jpacomposite;

import com.example.jpacomposite.entity.Item;
import com.example.jpacomposite.entity.ProdPlan;
import jakarta.persistence.TypedQuery;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
public class ItemAndProdPlanTest {

    @Autowired
    private TestEntityManager entityManager;

    @Test
    public void testFindProdPlanWithItem() {
        Item item = new Item();
        item.setFactoryCode("FAC001");
        item.setMaterialCode("MAT001");
        item.setMaterialName("Material 1");
        entityManager.persist(item);

        ProdPlan prodPlan = new ProdPlan();
        prodPlan.setFactoryCode("FAC001");
        prodPlan.setProdPlanId("PP001");
        prodPlan.setItemCode(item.getMaterialCode());
        entityManager.persist(prodPlan);

        entityManager.flush();
        entityManager.clear();

        TypedQuery<ProdPlan> query = entityManager.getEntityManager().createQuery("SELECT p FROM ProdPlan p WHERE p.factoryCode = :factoryCode AND p.prodPlanId = :prodPlanId", ProdPlan.class);
        query.setParameter("factoryCode", "FAC001");
        query.setParameter("prodPlanId", "PP001");
        List<ProdPlan> resultList = query.getResultList();

        assertThat(resultList).hasSize(1);
        ProdPlan foundProdPlan = resultList.get(0);
        assertThat(foundProdPlan.getItem()).isNotNull();
        assertThat(foundProdPlan.getItem().getFactoryCode()).isEqualTo(item.getFactoryCode());
        assertThat(foundProdPlan.getItem().getMaterialCode()).isEqualTo(item.getMaterialCode());
        assertThat(foundProdPlan.getItem().getMaterialName()).isEqualTo(item.getMaterialName());
    }

}

감사합니다.

이혜정님의 프로필 이미지
이혜정
질문자

아 이해했습니다 readOnly로 매핑을 하는 수 밖에 없군요 감사합니다!

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 이혜정님

질문이 명확하게 잘 이해가 되지 않습니다.

다음 내용을 적어주시면 좋겠습니다.

  1. create table DDL을 바로 테이블을 생성할 수 있을 정도로 정확하게 적어주세요. 테스트를 위해서 강의에 나온 H2 데이터베이스를 기준으로 만들 수 있도록 해주세요.

  2. create table DDL에서 PK, FK 제약조건도 포함해주세요.

  3. 두 테이블을 조인해서 조회하는 SQL을 적어주세요. 실행가능한 SQL이어야 합니다.

  4. 샘플 예제 INSERT SQL을 넣고, 조인해서 조회하는 SQL도 남겨주세요. 그리고 그 결과가 어떻게 되는지도 남겨주세요.

감사합니다.

이혜정님의 프로필 이미지
이혜정
질문자

  1. create table ITEM (

    FACTORY_CODE varchar(6),

    MATERIAL_CODE varchar(20),

    MATERIAL_NAME varchar(30),

    constraint PK_ITEM primary key(FACTORY_CODE, MATERIAL_CODE)

    )

    create table PRODPLAN (

    FACTORY_CODE varchar(6),

    PRODPLAN_ID varchar(20),

    ITEM_CODE varchar(20),

    constraint PK_PRODPLAN primary key(FACTORY_CODE, PRODPLAN_ID),

    foreign key (FACTORY_CODE, ITEM_CODE) references ITEM(FACTORY_CODE, MATERIAL_CODE)

    )

     

  2. 위에 포함했습니다!

  3. select * from prodplan a

    left join item b

    on a.factory_code = b.factory_code

    and a.item_code = b.material_code

  4. insert into item values ('000001', '00010', '치약')

    insert into prodplan values ('000001', '202304170001', '00010')

FACTORY_CODE는 ITEM과 PRODPLAN의 복합키 중 하나입니다!

감사합니다.

김영한님의 프로필 이미지
김영한
지식공유자

답글 남겨두었습니다^^

이혜정님의 프로필 이미지
이혜정

작성한 질문수

질문하기