• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

복합키 끼리의 매핑 질문 드립니다.

23.09.21 11:05 작성 조회수 391

1

안녕하세요.

현재 JPA강의를 통해 회사내 프로젝트중인 직장인 입니다.

실무 중에 강의의 복합키 매핑과는 조금 다른 내용이 있어 며칠을 고민하다 문의 드립니다.

 

  • 각각 복합키를 가진 두 개의 테이블이 있습니다.

  • 두 테이블은 code라는 공통 컬럼이있고 다대일관계로 매핑을 구성하려고 합니다.

  • @EmbeddedId관계를 이용해 구성하려고 합니다.

  • code 컬럼만 매핑시키는 방법을 찾지 못해 문의 남겨드립니다.

 

현재 문제의 테이블입니다.(회사프로젝트 테이블이라 자세히 올려드리는 못하는 점 양해 부탁드립니다.)

  1. Company Table은 code, biznumber 두 개의 pk로 구성돼 있습니다.

  2. Contract Table 역시 code, module 두 개의 pk로 구성돼 있습니다.

  3. 저는 두 테이블에서 code라는 컬럼만 갖고 다대일 매핑을 구현하고 싶습니다.

  • 즉 복합키를 각각 가진 두 개의 테이블에서 각각 하나의 컬럼만을 이용해 다대일 매핑을 구현하고 싶습니다.

  • 제가 에러 내용과 과정을 올리고싶은데 회사코드라 올리면 문제가 생길까 싶어 이렇게 말로 표현드려 죄송합니다.

 

 

 

답변 2

·

답변을 작성해보세요.

1

안녕하세요. 차재욱님

몇가지 제약이 있기는 하지만 가능은 합니다.

우선 이렇게 적용하려면 복합키는 @IdClass 방식을 사용해야 할 것 같아요.

(실무에서도 복합키 매핑은 @IdClass를 더 권장합니다.)

다음 코드를 참고해주세요.

package com.example.jpacomposite.code2;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import lombok.Data;

@Data
@IdClass(CompanyPK.class)
@Entity
public class Company {

    @Id
    private String code;

    @Id
    private String biznumber;
}

 

package com.example.jpacomposite.code2;

import lombok.Data;

import java.io.Serializable;

@Data
public class CompanyPK implements Serializable {
	private String code;
	private String biznumber;

	public CompanyPK() {
	}

	public CompanyPK(String code, String biznumber) {
		this.code = code;
		this.biznumber = biznumber;
	}
}

 

package com.example.jpacomposite.code2;

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


@Data
@IdClass(ContractPK.class)
@Entity
public class Contract {

    @Id
    private String companyCode;

    @Id
    private String moduleCode;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "companyCode", referencedColumnName = "code", insertable = false, updatable = false)
    private Company company;
}
package com.example.jpacomposite.code2;

import lombok.Data;

import java.io.Serializable;

@Data
public class ContractPK implements Serializable {
	private String companyCode;
	private String moduleCode;

	public ContractPK() {
	}

	public ContractPK(String companyCode, String moduleCode) {
		this.companyCode = companyCode;
		this.moduleCode = moduleCode;
	}
}

 

package com.example.jpacomposite.code2;

import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

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

@DataJpaTest
class CompanyTest2 {

    @Autowired
    EntityManager em;

    @Test
    void go() {
        Company company = new Company();
        company.setCode("code1");
        company.setBiznumber("biznum1");
        em.persist(company);

        Contract contract = new Contract();
        contract.setCompanyCode("code1");
        contract.setModuleCode("module1");
        em.persist(contract);

        em.flush();
        em.clear();

        ContractPK pk = new ContractPK("code1", "module1");
        Contract findContract = em.find(Contract.class, pk);
        System.out.println("findContract = " + findContract);
        Company findCompany = findContract.getCompany();
        assertThat(findCompany.getCode()).isEqualTo(company.getCode());
        assertThat(findCompany.getBiznumber()).isEqualTo(company.getBiznumber());

        em.flush();
    }
}

 

여기서 Contract 클래스의 매핑 부분이 중요한데요.

연관관계를 사용할 수는 있지만 이 연관관계는 읽기 전용으로만 사용하셔야 합니다. 왜냐하면 연관관계가 이미 PK를 통해서 모두 확정이 되어 버리기 때문입니다.

이 필드를 통해서는 연관관계를 읽기만 가능한 것이지요.

 

AI 인턴에 달린 댓글을 저희가 확인을 못해서 답변이 많이 늦게 되었습니다. 죄송합니다.

그리고 도움이 되셨길 바래요 :)

차재욱님의 프로필

차재욱

질문자

2023.10.20

아닙니다. 선생님 바쁘신 와중에 이렇게 답글 달아주셔서 감사드립니다. 덕분에 해당 문제 해결했습니다. 앓던 이가 빠진 기분입니다. ㅠㅠㅠ 너무 감사드립니다.

0

인프런 AI 인턴님의 프로필

인프런 AI 인턴

2023.09.23

안녕하세요, 인프런 AI 인턴입니다.
복합키를 가진 두 개의 테이블에서 code 컬럼만을 이용해 다대일 매핑을 구현하고 싶으시군요. 일반적으로는 @EmbeddedId 어노테이션을 사용하여 복합키를 매핑할 수 있습니다.

@Entity
public class Company {

@EmbeddedId
private CompanyId id;

// 다른 필드들
// ...

}

@Embeddable
public class CompanyId implements Serializable {

private String code;

// getter, setter, equals, hashCode 등 구현
// ...

}

@Entity
public class Contract {

@EmbeddedId
private ContractId id;

// 다른 필드들
// ...

}

@Embeddable
public class ContractId implements Serializable {

private String code;

// getter, setter, equals, hashCode 등 구현
// ...

}

위와 같이 코드를 작성하고, Company와 Contract의 code 필드에 @ManyToOne 어노테이션을 추가하여 다대일 관계를 매핑할 수 있습니다.

그러나 복합키를 가진 테이블에서 일부 컬럼만으로 다대일 매핑을 구현하는 것은 JPA의 명세에 규정된 방법이 아니기 때문에, 일부 엔티티 매니저 구현체에서는 지원하지 않을 수 있습니다. 이런 경우에는 별도의 조인 컬럼을 추가하는 방법을 고려해보실 수도 있습니다.

혹시 이렇게 구현했을 때 에러가 발생하는지에 대한 내용을 자세히 알고 싶으시다면, 본문에 언급하신 회사 코드 등의 민감한 정보를 표현하지 않는 형태로 구체적인 에러 내용과 과정을 공유해주시면 더 정확한 도움을 드릴 수 있을 것 같습니다.
더 궁금한 점이 있으시면 언제든지 물어보세요!

차재욱님의 프로필

차재욱

질문자

2023.09.25

안녕하세요.

먼저 답변 달아주셔서 감사드립니다.

해당 내용을 JPA에서 지원하지 않는다는 점 확인했습니다.

그럼에도 아무래도 필요하다보니 가능하다면 진행해 보고싶은 마음에 이렇게 다시 문의 드립니다.

 

먼저 달아주신 답변 기반으로 진행한 코드 및 결과 첨부해드립니다.

@Embeddable

CompanyPK.Class

public class CompanyPK implements Serializable{
	private String code;
	private String biznumber;
}

 

@Entity

Company.Class

public class Company extends BaseEntity{	
	@EmbeddedId
	private CompanyPK companyId;
}

 

@Embeddable

ContractPK.Class

public class ContractPK implements Serializable{
	private String companyCode;
	private String moduleCode;
}

 

@Entity

Contract.Class

public class Contract extends BaseEntity{		
	@EmbeddedId
	private ContractPK contractId;
	
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="company_code")
	private Company company;
}

Contract Class에서 Company Class와 다대일 매핑 시도시

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: An association that targets entity 'entity.Company' from entity 'entity.Contract' has 1 '@JoinColumn's but the primary key has 2 columns

와 같은 에러가 발생합니다.

 

다음은

	@MapsId("companyCode")
	@ManyToOne(fetch=FetchType.LAZY)
	@JoinColumn(name="company_code")
	private Company company;

위의 Contract.Class에서 @MapsId("companyCode") 추가시 실행하면

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Property 'company' of entity 'entity.Contract' must have a '@JoinColumn' which references the foreign key column 'null'

이런 오류가 발생합니다.

 

오류 내용일 짐작해보아 하나는 'PK가 2개인데 1개를 찾을 수 없다'로 보이고 나머지 하나는 JoinColumn을 못찾는 듯 합니다.

 

어떤 부분을 개선해야 되는지 알 수 있을까요?

 

또한 복합키에서 @EmbeddedId를 사용해야되는 이유가 있나요?

간단하게 필드에서 @Id, @Id 이렇게 두개로 구현하면 안되는걸까요?

추가 궁금증에 관한 문의 내용도 남겨드립니다.

 

감사합니다. :D