web-dev-qa-db-ja.com

JPA @OneToManyアソシエーションを使用するときの@JoinColumnとmappedByの違いは何ですか

違いは何ですか:

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

@JoinColumnは、関係の両側で使用できます。 @JoinColumn側で@OneToManyを使用することについての質問です(まれなケース)。そしてここで重要なのは、物理情報の重複(列名)と追加のUPDATEステートメントを生成する最適化されていないSQLクエリです。

ドキュメンテーション :によると

多対1は (ほぼ)常にJPA仕様の双方向関係の 所有者側 であるため、1対多関連は@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() {
    ...
} 

部隊は部隊の特性を通して兵士と双方向の1対多の関係を持っています。 mappedBy側で物理マッピングを定義する必要はありません(してはいけません)。

1対多側を所有側 として双方向の1対多をマッピングするには、mappedBy要素を削除し、その多対1を挿入可能および更新可能としてfalseに更新する必要があります。この解決策は最適化されておらず、追加の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() {
    ...
}
193

アノテーション@JoinColumnは、このエンティティがリレーションシップの owner であることを示します(つまり、対応するテーブルは参照先テーブルへの外部キーを持つ列を持ちます)。一方、属性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;
}
500
Óscar López

この記事 で説明したように、@OneToManyアノテーションを@JoinColumnと一緒に使用すると、単方向関連になります。

mappedBy属性を設定して@OneToManyを使用する場合は、双方向の関連付けがあります。つまり、mappedByが参照する@ManyToOne関連付けを子側に持つ必要があります。

単方向@OneToManyアソシエーションはあまりうまく動作しません したがって、避けるべきです。

もっと効率的な bidirectional @OneToManyを使うほうがいいです

34
Vlad Mihalcea

アノテーション mappedBy は理想的には常に双方向の関係の親側(Companyクラス)で使われるべきです、この場合それはChildクラス(Branchクラス)のメンバー変数 'c​​ompany'を指すCompanyクラスにあるべきです)

アノテーション @JoinColumn はエンティティの関連付けに参加するためのマップされた列を指定するために使われます。このアノテーションはどのクラス(ParentまたはChild)でも使うことができますここでは両方ともそうではない子クラスではここで私はブランチクラスの外部キーを示す双方向の関係の子側(ブランチクラス)でそれを使用しました。

以下は実用的な例です。

親クラス、会社

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


}
31
Coral

this の答えが示すように、@JoinColumnは必ずしも物理的な情報の場所に関連付ける必要はないことを付け加えたいと思います。親テーブルに子テーブルを指すテーブルデータがない場合でも、@JoinColumn@OneToManyと組み合わせることができます。

JPAで単方向のOneToMany関係を定義する方法

単方向のOneToMany、逆のManyToOne、結合のない表

JPA 2.x+でしか利用できないようです。参照全体ではなく、子クラスに親のIDのみを含めるようにしたい場合に便利です。

19
Snekse

私はここオスカルロペスによって受け入れられた答えに同意しません。その答えは不正確です。

このエンティティがリレーションシップの所有者であることを示すのはNOT @JoinColumnです。代わりに、これを行うのは@ManyToOneアノテーションです(彼の例では)。

@ManyToOne@OneToMany@ManyToManyなどの関係アノテーションはJPA/Hibernateに マッピングを作成するように伝えます。 デフォルトでは、これは別々の結合テーブルを介して行われます。


@JoinColumn

@JoinColumnの目的は、 結合列 が存在しない場合はそれを作成することです。もしそうなら、このアノテーションは name joinカラムに使うことができます。


MappedBy

MappedByパラメータの目的はJPAに指示することです。この関係の 反対 エンティティによってすでに関係がマップされているので、別の結合テーブルを作成しないでください。



覚えておいてください:MappedByは、デフォルトでは結合テーブルを作成することによって2つのエンティティを関連付けるメカニズムを生成することを目的としたリレーションシップアノテーションのプロパティです。 MappedByはそのプロセスを一方向に停止させます。

マッピングの仕組みは外部キーフィールドに対する3つのマッピング注釈のうちの1つを使用することによってそのクラス内で決定されるため、MappedByを使用しないエンティティは関係の owner と呼ばれます。これはマッピングの性質を指定するだけでなく、結合テーブルの作成も指示します。さらに、結合テーブルを抑制するオプションは、外部キーに@JoinColumnアノテーションを適用することによっても存在します。これにより、代わりに所有者エンティティのテーブル内に保持されます。

要約すると、@JoinColumnは新しい結合列を作成するか、既存の結合列の名前を変更します。一方、MappedByパラメータは、結合テーブルを介して、または所有者エンティティの関連テーブルに外部キー列を作成することによってマッピングを作成するために、他の(子)クラスのリレーションシップアノテーションと共同で機能します。

MapppedByのしくみを説明するために、以下のコードを検討してください。 MappedByパラメータを削除すると、Hibernateは実際には2つの結合テーブルを作成します。どうして?多対多の関係には対称性があり、Hibernateには一方向を他の方向よりも選択する根拠がありません。

したがって、HibernateにMappedByを使用し、2つのエンティティ間の関係のマッピングを指示するために 他の エンティティを選択しました。

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

JPAは階層化されたAPIです。さまざまなレベルに独自の注釈があります。最高レベルは(1)永続クラスを記述するEntityレベルです。それから、(2)エンティティがリレーショナルデータベースにマッピングされると仮定する(2)リレーショナルデータベースレベル、および(3)Javaモデルがあります。

レベル1の注釈:@Entity、@ Id、@ OneToOne、@ OneToMany、@ManyToOne、@ManyToMany。これらの高レベルの注釈だけを使用して、アプリケーションに永続性を導入することができます。しかし、その場合はJPAの仮定に従ってデータベースを作成する必要があります。これらの注釈は実体/関係モデルを指定します。

レベル2アノテーション:@Table、@Column、@JoinColumn、... JPAのデフォルトに満足できない場合、または既存のデータベースにマップする必要がある場合は、エンティティ/プロパティからリレーショナルデータベースのテーブル/列へのマッピングに影響を与えます。これらのアノテーションは実装アノテーションとして見ることができ、それらはマッピングがどのように行われるべきかを指定します。

私の意見では、できるだけ高水準の注釈に固執し、次に必要に応じて低水準の注釈を導入するのが最善です。

質問に答えるには、@ OneToMany/mappedByはエンティティドメインからの注釈のみを使用するため、最も優れています。 @ oneToMany/@ JoinColumnも問題ありませんが、厳密には必要ない場合は実装アノテーションを使用します。

0