JPA @OneToMany 연관 사용시 @JoinColumn과 MappingBy의 차이점


516

차이점은 무엇입니까?

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
    @JoinColumn(name = "companyIdRef", referencedColumnName = "companyId")
    private List<Branch> branches;
    ...
}

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY, mappedBy = "companyIdRef")
    private List<Branch> branches;
    ...
}

1
또한 관련된 문제에 대한 올바른 설명은 ORM 매핑 질문 에서 소유 측이 무엇입니까?를 참조하십시오 .
dirkt

답변:


545

주석은 @JoinColumn이 엔티티가 관계 의 소유자 임을 나타냅니다 (즉, 해당 테이블에 참조 된 테이블에 대한 외래 키가있는 열이 mappedBy있음 ). 반면이 특성 의 엔티티는 관계의 반대임을 나타냅니다. 소유자는 "기타"엔티티에 상주합니다. 이것은 또한 "mappedBy"(완전한 양방향 관계)로 주석이 달린 클래스에서 다른 테이블에 액세스 할 수 있음을 의미합니다.

특히 문제의 코드에서 올바른 주석은 다음과 같습니다.

@Entity
public class Company {
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "company")
    private List<Branch> branches;
}

@Entity
public class Branch {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "companyId")
    private Company company;
}

3
두 경우 모두 지점에는 회사 ID가있는 필드가 있습니다.
Mykhaylo Adamovych

3
회사 테이블에 참조 테이블에 대한 외래 키가있는 열이 없습니다. 분기가 회사를 참조합니다. 왜 "해당 테이블에 참조 테이블에 대한 외래 키가있는 열이 있습니다"라고 말합니까? 좀 더 설명해 주시겠습니까?
Mykhaylo Adamovych

13
@ MykhayloAdamovych 샘플 코드로 답변을 업데이트했습니다. 공지 사항이 사용하는 실수 있음 @JoinColumnCompany
오스카르 로페스

10
@ MykhayloAdamovych : 아니요, 실제로는 정확하지 않습니다. 경우 Branch속성 참조가없는 Company,하지만 기본 테이블이하는 열을 가지고, 당신은 사용할 수 있습니다 @JoinTable매핑 할 수 있습니다. 일반적으로 테이블에 해당하는 개체의 열을 매핑하지만 발생할 수 있으며 완벽하게 합법적이므로 예외적 인 상황입니다.
Tom Anderson

4
이것이 ORM을 좋아하지 않는 또 다른 이유입니다. 문서는 종종 너무 까다 롭고 내 책에서 너무 많은 마법 영역을 의미합니다. 나는이 문제로 어려움을 겪고 있으며 단어에 대해 단어별로 단어를 따라 @OneToOne가면 자식 행이 null부모를 참조하는 FKey 열에서 업데이트됩니다 .
Ashesh

225

@JoinColumn관계의 양쪽에 사용될 수 있습니다. 문제는 측면에서 사용 @JoinColumn하는 것에 관한 것이 었습니다 @OneToMany(드문 경우). 그리고 여기서 요점은 실제 정보 복제 (열 이름)와 일부 추가 UPDATE명령문 을 생성하는 최적화되지 않은 SQL 쿼리입니다 .

설명서 에 따르면 :

이후 하나 많은된다 (거의) 항상 오너 측 JPA에 사양의 쌍방향 관계가 많은 관계로 하나에 의해 주석된다@OneToMany(mappedBy=...)

@Entity
public class Troop {
    @OneToMany(mappedBy="troop")
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk")
    public Troop getTroop() {
    ...
} 

TroopSoldier병력 속성 을 통해 양방향 일대 다 관계가 있습니다. mappedBy측면 에서 물리적 매핑을 정의 할 필요는 없습니다 .

으로, 많은에 양방향 하나를 매핑하려면 소유 측면으로 일대면 , 당신은 제거해야 할 mappedBy요소를 하나에 많은 설정 @JoinColumn으로 insertable하고 updatablefalse로. 이 솔루션은 최적화되지 않았으며 몇 가지 추가 UPDATE설명 이 생성됩니다 .

@Entity
public class Troop {
    @OneToMany
    @JoinColumn(name="troop_fk") //we need to duplicate the physical information
    public Set<Soldier> getSoldiers() {
    ...
}

@Entity
public class Soldier {
    @ManyToOne
    @JoinColumn(name="troop_fk", insertable=false, updatable=false)
    public Troop getTroop() {
    ...
}

1
두 번째 스 니펫에서 Troop을 어떻게 소유 할 수 있는지 알 수 없습니다. Soldier는 Troop을 참조하는 외래 키를 포함하고 있기 때문에 여전히 소유자입니다. (나는 mysql을 사용하고 있으며, 당신의 접근 방식으로 확인했다).
Akhilesh

10
예에서 주석은 mappedBy="troop"어떤 필드를 가리 킵 니까?
Fractaliste

5
@Fractaliste 주석은 mappedBy="troop"Soldier 클래스의 속성 부대를 나타냅니다. 위의 코드에서는 Mykhaylo가 생략했기 때문에 속성이 표시되지 않지만 getter getTroop ()에 의해 해당 속성의 존재를 추론 할 수 있습니다. Óscar López 의 답변을 확인하십시오. 매우 명확하며 포인트를 얻습니다.
nicolimo86

1
이 예는 JPA 2 사양을 남용한 것입니다. 작성자의 목표가 양방향 관계를 작성하는 것이라면 부모 측에서 mappingBy를 사용하고 자식 측에서는 JoinColumn (필요한 경우)을 사용해야합니다. OneToMany 및 ManyToOne 그 둘의 관계는 같은 외래 키 사용하여 정의됩니다 (자세한 오용에 의한) 독립하지만 단지 운에 의해 다음과 같습니다 접근 방식은 여기에 제시된으로 우리는 2 개 단방향 관계지고있다
aurelije

1
JPA 2.x를 사용하는 경우 아래 답변이 약간 더 깨끗합니다. 두 경로를 모두 시도하고 테이블을 생성 할 때 Hibernate 가하는 일을 보는 것이 좋습니다. 새로운 프로젝트를 진행 중이라면 필요에 맞는 세대를 선택하십시오. 레거시 데이터베이스에 있고 구조를 변경하지 않으려면 스키마와 일치하는 것을 선택하십시오.
Snekse

65

이것은 매우 일반적인 질문 이므로이 답변을 기반으로하는이 기사를 작성 했습니다.

단방향 일대 다 연결

이 기사 에서 설명했듯이 @OneToMany와 함께 주석 을 사용 하면 다음 다이어그램 @JoinColumn의 상위 Post엔터티와 하위 엔터티와 같은 단방향 연결이 있습니다 PostComment.

단방향 일대 다 연결

단방향 일대 다 연관을 사용하는 경우 상위 측만 연관을 맵핑합니다.

이 예에서는 Post엔터티 만 @OneToMany하위 PostComment엔터티에 대한 연결을 정의합니다 .

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "post_id")
private List<PostComment> comments = new ArrayList<>();

양방향 일대 다 연관

속성 세트 @OneToMany와 함께 with 를 사용하면 mappedBy양방향 연관이 있습니다. 이 경우 엔터티에는 다음 그림 과 같이 자식 엔터티 Post모음이 PostComment있고 자식 PostComment엔터티에는 부모 엔터티에 대한 참조가 있습니다 Post.

양방향 일대 다 연관

에서 PostComment엔티티는 post다음과 같이 개체 속성 매핑됩니다 :

@ManyToOne(fetch = FetchType.LAZY)
private Post post;

fetch속성을 명시 적으로 설정 한 이유는 FetchType.LAZY기본적으로 모든 연결 @ManyToOne@OneToOne연결을 열심히 가져 와서 N + 1 쿼리 문제를 일으킬 수 있기 때문입니다. 이 주제에 대한 자세한 내용은 이 기사를 확인 하십시오 .

에서 Post엔티티는 comments다음과 같이 연관 매핑된다 :

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();

mappedBy의 속성 @OneToMany주석을 참조합니다 post자식 재산 PostComment법인과이 방법은, Hibernate는 양방향 연관이에 의해 제어되는 것을 알고 @ManyToOne이 테이블 관계에 기반으로하는 외래 키 컬럼 값을 관리하는 담당 측면.

양방향 연결을 위해서는 addChildand와 같은 두 가지 유틸리티 방법이 필요합니다 removeChild.

public void addComment(PostComment comment) {
    comments.add(comment);
    comment.setPost(this);
}

public void removeComment(PostComment comment) {
    comments.remove(comment);
    comment.setPost(null);
}

이 두 가지 방법은 양방향 연결의 양쪽이 동기화되지 않도록합니다. 양쪽 끝을 동기화하지 않으면 Hibernate는 연결 상태 변경이 데이터베이스로 전파 될 것이라고 보장하지 않습니다.

JPA 및 Hibernate와 양방향 연관을 동기화하는 최상의 방법에 대한 자세한 내용은 이 기사를 확인 하십시오 .

어느 것을 선택해야합니까?

단방향 @OneToMany협회는 매우 잘 수행하지 않습니다 당신이 그것을 피해야한다, 그래서.

보다 효율적인 양방향 @OneToMany 사용하는 것이 좋습니다 .


32

맵핑 된 주석은 이상적으로 항상 양방향 관계의 상위 측 (회사 클래스)에서 사용되어야하며,이 경우 하위 클래스의 멤버 변수 '회사'(브랜치 클래스)를 가리키는 회사 클래스에 있어야합니다.

@JoinColumn 어노테이션 은 엔티티 연관을 결합하기 위해 맵핑 된 열을 지정하는 데 사용 되며이 어노테이션은 모든 클래스 (Parent 또는 Child)에서 사용할 수 있지만 한쪽에서만 (부모 클래스 또는 Child 클래스가 아닌) 이상적으로 사용되어야합니다. 두 경우 모두)이 경우에는 Branch 클래스의 외래 키를 나타내는 양방향 관계의 자식 측 (지점 클래스)에서 사용했습니다.

아래는 작업 예입니다.

부모 클래스, 회사

@Entity
public class Company {


    private int companyId;
    private String companyName;
    private List<Branch> branches;

    @Id
    @GeneratedValue
    @Column(name="COMPANY_ID")
    public int getCompanyId() {
        return companyId;
    }

    public void setCompanyId(int companyId) {
        this.companyId = companyId;
    }

    @Column(name="COMPANY_NAME")
    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }

    @OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="company")
    public List<Branch> getBranches() {
        return branches;
    }

    public void setBranches(List<Branch> branches) {
        this.branches = branches;
    }


}

어린이 교실, 지사

@Entity
public class Branch {

    private int branchId;
    private String branchName;
    private Company company;

    @Id
    @GeneratedValue
    @Column(name="BRANCH_ID")
    public int getBranchId() {
        return branchId;
    }

    public void setBranchId(int branchId) {
        this.branchId = branchId;
    }

    @Column(name="BRANCH_NAME")
    public String getBranchName() {
        return branchName;
    }

    public void setBranchName(String branchName) {
        this.branchName = branchName;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="COMPANY_ID")
    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }


}

20

답변이 제안하는 것처럼 @JoinColumn항상 물리적 정보 위치 와 관련이있을 필요는 없다는 것을 추가하고 싶습니다 . 당신은 결합 할 수 있습니다 와 부모 테이블 자식 테이블을 가리키는에는 테이블 데이터가없는 경우에도 마찬가지입니다.@JoinColumn@OneToMany

JPA에서 단방향 OneToMany 관계를 정의하는 방법

단방향 OneToMany, 반대 ManyToOne, 조인 테이블 없음

JPA 2.x+그래도 사용할 수있는 것 같습니다 . 자식 클래스에 참조의 전체가 아닌 부모의 ID 만 포함시키려는 상황에 유용합니다.


맞습니다. 조인 테이블이없는 단방향 OneToMany에 대한 지원이 JPA2에 도입되었습니다.
aurelije

17

나는 Óscar López의 대답에 동의하지 않습니다. 그 대답은 정확하지 않습니다!

@JoinColumn이 엔티티가 관계의 소유자임을 나타내는 것은 아닙니다 . 대신 @ManyToOne(이 예제에서)이 작업을 수행 하는 주석입니다.

의 관계는 다음과 같은 주석을 @ManyToOne, @OneToMany그리고 @ManyToMany할 JPA / 최대 절전 모드를 말할 매핑을 만듭니다. 기본적으로이 작업은 별도의 조인 테이블을 통해 수행됩니다.


@JoinColumn

조인 열 이 아직없는 경우 @JoinColumn이를 만드는 것이 목적입니다 . 그렇다면이 주석을 사용 하여 조인 열의 이름을 지정할 수 있습니다 .


매핑

MappedBy매개 변수 의 목적은 JPA에 지시 하는 것입니다 . 관계가이 관계 의 반대 엔티티에 의해 이미 맵핑되고 있으므로 다른 결합 테이블을 작성하지 마십시오 .



기억하십시오 : MappedBy기본적으로 조인 테이블을 작성하여 수행하는 두 엔티티를 연관시키는 메커니즘을 생성하는 것이 목적인 관계 주석의 특성입니다. MappedBy해당 프로세스를 한 방향으로 중지합니다.

외래 키 필드에 대한 세 가지 매핑 주석 중 하나를 사용하여 매핑의 메커니즘이 클래스 내에서 지시되기 때문에 사용하지 않는 엔터티 는 관계 MappedBy소유자 라고 합니다. 이는 맵핑의 특성을 지정할뿐만 아니라 결합 테이블 작성을 지시합니다. 또한 조인 테이블을 억제하는 옵션은 외래 키에 @JoinColumn 주석을 적용하여 소유자 엔터티의 테이블 내에 대신 유지함으로써도 존재합니다.

요약하면 : @JoinColumn새 조인 열을 만들거나 기존 열의 이름을 바꿉니다. 이 MappedBy매개 변수는 조인 테이블을 통해 또는 소유자 엔티티의 연관된 테이블에 외래 키 열을 작성하여 맵핑을 작성하기 위해 다른 (하위) 클래스의 관계 주석과 협력하여 작동합니다.

MapppedBy작동 방식 을 설명하려면 아래 코드를 고려하십시오. 경우 MappedBy매개 변수를 삭제했다, 다음 최대 절전 모드가 실제로 만들 것 두 테이블을 조인! 왜? 다 대다 관계에는 대칭이 있기 때문에 Hibernate는 한 방향을 다른 방향으로 선택하는 데 대한 근거가 없습니다.

그러므로 MappedBy우리는 Hibernate에게 말하기 위해 사용 한다. 우리는 다른 엔티티를 선택 하여 두 엔티티 사이의 관계의 맵핑을 지시했다.

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    private List<Drivers> drivers;
}

소유자 클래스에 @JoinColumn (name = "driverID")을 추가하면 (아래 참조) 조인 테이블을 만들지 않고 대신 Cars 테이블에 driverID 외래 키 열을 만들어 매핑을 구성합니다.

@Entity
public class Driver {
    @ManyToMany(mappedBy = "drivers")
    private List<Cars> cars;
}

@Entity
public class Cars {
    @ManyToMany
    @JoinColumn(name = "driverID")
    private List<Drivers> drivers;
}

1

JPA는 계층화 된 API이며 다른 수준에는 자체 주석이 있습니다. 최상위 레벨은 (1) 지속성 클래스를 설명하는 엔티티 레벨이며, 엔티티가 관계형 데이터베이스 및 (3) Java 모델로 맵핑되는 것으로 가정하는 (2) 관계형 데이터베이스 레벨이 있습니다.

레벨 1 주석 : @Entity, @Id, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany. 이러한 고급 주석 만 사용하여 애플리케이션에 지속성을 도입 할 수 있습니다. 그러나 JPA의 가정에 따라 데이터베이스를 작성해야합니다. 이러한 주석은 엔티티 / 관계 모델을 지정합니다.

레벨 2 주석 : @Table, @Column, @JoinColumn, ... 관계형 데이터베이스 테이블에 엔티티 / 속성의 매핑에 영향을 / 당신은 JPA의 기본값에 만족하지 않거나 기존 데이터베이스에 매핑해야하는 경우 경우 열. 이러한 주석은 구현 주석으로 볼 수 있으며 매핑 수행 방법을 지정합니다.

제 생각에는 가능한 한 높은 수준의 주석에 충실한 다음 필요에 따라 낮은 수준의 주석을 도입하는 것이 가장 좋습니다.

질문에 대답하려면 : @OneToMany/ mappedBy는 엔티티 도메인의 주석 만 사용하므로 가장 좋습니다. @oneToMany/는 @JoinColumn또한 미세하지만 이것이 꼭 필요한 것은 구현 주석을 사용합니다.


1

간단하게하겠습니다. 매핑에 관계없이 양쪽에서 @JoinColumn
사용할 수 있습니다 .

이것을 세 가지 경우로 나누겠습니다.
1) 지점에서 회사로 단방향 매핑.
2) 회사에서 지점으로의 양방향 매핑.
3) 회사에서 지점으로 단방향 매핑 만.

따라서 모든 유스 케이스는이 세 가지 범주에 속합니다. @JoinColumnMappingBy 사용법을 설명하겠습니다 .
1) 지점에서 회사로 단방향 매핑. 분기 테이블에서 JoinColumn
사용하십시오 . 2) 회사에서 지점으로의 양방향 매핑. 사용 mappedBy 회사 테이블은 @Mykhaylo Adamovych의 대답으로 설명합니다. 3) 회사에서 지점으로의 단방향 매핑. 회사 테이블에서 @JoinColumn 을 사용 하십시오.



@Entity
public class Company {

@OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
@JoinColumn(name="courseId")
private List<Branch> branches;
...
}

이것은 분기 테이블의 외래 키 "courseId"매핑을 기반으로 모든 분기 목록을 가져옵니다. 참고 :이 경우 지점에서 회사를 페치 할 수 없으며 회사 간에는 단방향 맵핑 만 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.