どの場合にJPA @JoinTableアノテーションを使用しますか?


回答:


350

編集2017-04-29:一部のコメンターが指摘しているように、このJoinTable例ではmappedBy注釈属性は必要ありません。実際、Hibernateの最近のバージョンでは、次のエラーを出力して起動を拒否しています。

org.hibernate.AnnotationException: 
   Associations marked as mappedBy must not define database mappings 
   like @JoinTable or @JoinColumn

名前の付いたエンティティと名前の付いたProject別のエンティティがTaskあり、各プロジェクトが多くのタスクを持つことができるとしましょう。

このシナリオのデータベーススキーマは、2つの方法で設計できます。

最初の解決策は、というテーブルとという名前のProject別のテーブルを作成し、という名前Taskのタスクテーブルに外部キー列を追加することproject_idです。

Project      Task
-------      ----
id           id
name         name
             project_id

このようにして、タスクテーブルの各行のプロジェクトを決定することができます。このアプローチを使用する場合、エンティティクラスでは結合テーブルは必要ありません。

@Entity
public class Project {

   @OneToMany(mappedBy = "project")
   private Collection<Task> tasks;

}

@Entity
public class Task {

   @ManyToOne
   private Project project;

}

もう1つの解決策は、3番目のテーブル(例:)を使用して、Project_Tasksプロジェクトとタスク間の関係をそのテーブルに格納することです。

Project      Task      Project_Tasks
-------      ----      -------------
id           id        project_id
name         name      task_id

Project_Tasks表には、「結合テーブル」と呼ばれています。この2番目のソリューションをJPAに実装するには、@JoinTableアノテーションを使用する必要があります。たとえば、単方向の1対多の関連付けを実装するために、エンティティを次のように定義できます。

Project エンティティ:

@Entity
public class Project {

    @Id
    @GeneratedValue
    private Long pid;

    private String name;

    @JoinTable
    @OneToMany
    private List<Task> tasks;

    public Long getPid() {
        return pid;
    }

    public void setPid(Long pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Task> getTasks() {
        return tasks;
    }

    public void setTasks(List<Task> tasks) {
        this.tasks = tasks;
    }
}

Task エンティティ:

@Entity
public class Task {

    @Id
    @GeneratedValue
    private Long tid;

    private String name;

    public Long getTid() {
        return tid;
    }

    public void setTid(Long tid) {
        this.tid = tid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

これにより、次のデータベース構造が作成されます。

ER図1

@JoinTable注釈はまた、あなたが参加するテーブルのさまざまな側面をカスタマイズすることができます。たとえば、次のtasksようにプロパティに注釈を付けたとします。

@JoinTable(
        name = "MY_JT",
        joinColumns = @JoinColumn(
                name = "PROJ_ID",
                referencedColumnName = "PID"
        ),
        inverseJoinColumns = @JoinColumn(
                name = "TASK_ID",
                referencedColumnName = "TID"
        )
)
@OneToMany
private List<Task> tasks;

結果のデータベースは次のようになります。

ER図2

最後に、多対多の関連付けのスキーマを作成する場合は、結合テーブルの使用が唯一の利用可能なソリューションです。


1
最初のアプローチを使用して、マージ前に私のプロジェクトにタスクを入力し、各タスクに親プロジェクトを入力しますが、すべてのエントリはタスクの数に基づいて複製されます。2つのタスクを持つプロジェクトがデータベースに2回保存されます。どうして ?
MaikoID

UPDATEない私のデータベース内のエントリが複製され、休止状態は、左外部結合を選択していると私は知らないなぜ...
MaikoID

2
@JoinTable/@JoinColumn同じフィールドに注釈を付けることができると思いますmappedBy。したがって、正しい例は、mappedByinを維持しProject@JoinColumntoをTask.project (またはその逆)に移動することです
Adrian Shum

2
いいね!しかし、私はさらに質問がある:表が参加した場合にProject_Tasks必要とnameTask3つの列となる、だけでなく、: 、project_idtask_idtask_nameどのようにこれを達成するには?
マセマー2015年

5
このエラーを防ぐために、2番目の使用例でmapByを使用すべきではないと思いますCaused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
karthik m

14

@JoinTableエンティティが、異なるタイプの親を持ついくつかの親子関係の子になる可能性がある場合にも使用すると、よりクリーンになります。Behrangの例でフォローアップするには、タスクがプロジェクト、人、部門、研究、およびプロセスの子になることができると想像してください。

必要があるtaskテーブルには、5つの持っているnullable外部キー・フィールドを?私はそうは思わない...


14

これは、ManyToMany関連付けをマップする唯一のソリューションです。関連付けをマップするには、2つのエンティティテーブル間に結合テーブルが必要です。

多面のテーブルに外部キーを追加したくない場合、OneToMany(通常は単方向)アソシエーションにも使用され、片面から独立させます。

説明と例については、Hibernateドキュメントで@JoinTableを検索してください。


4

多対多の関係を処理できます。例:

Table 1: post

post has following columns
____________________
|  ID     |  DATE   |
|_________|_________|
|         |         |
|_________|_________|

Table 2: user

user has the following columns:

____________________
|     ID  |NAME     |
|_________|_________|
|         |         |
|_________|_________|

テーブルの結合では、次を使用してマッピングを作成できます。

@JoinTable(
  name="USER_POST",
  joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="ID"),
  inverseJoinColumns=@JoinColumn(name="POST_ID", referencedColumnName="ID"))

テーブルを作成します:

____________________
|  USER_ID| POST_ID |
|_________|_________|
|         |         |
|_________|_________|

1
質問:この追加のテーブルがすでにある場合はどうなりますか?JoinTableは既存のものを上書きしませんか?
TheWandererr

@TheWandererrあなたはあなたの質問に対する答えを見つけましたか?私はすでに結合テーブルを持っています
asgs

私の場合、それは所有側のテーブルに冗長な列を作成しています。たとえば。POSTのPOST_ID。なぜそれが起こっているのか提案できますか?
SPS

0

@ManyToMany 協会

ほとんどの場合、@JoinTableアノテーションを使用して、多対多のテーブルリレーションシップのマッピングを指定する必要があります。

  • リンクテーブルの名前と
  • 2つの外部キー列

したがって、次のデータベーステーブルがあるとします。

多対多のテーブルの関係

ではPostエンティティ、あなたはこのように、この関係をマップになります。

@ManyToMany(cascade = {
    CascadeType.PERSIST,
    CascadeType.MERGE
})
@JoinTable(
    name = "post_tag",
    joinColumns = @JoinColumn(name = "post_id"),
    inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();

@JoinTable注釈は、経由して、テーブル名を指定するために使用されてname参照する属性だけでなく、外部キー列postテーブル(例えば、joinColumns)との外部キー列post_tagのリンクテーブル参照することTagを通じて実体をinverseJoinColumns属性。

カスケード属性ということに注意してください@ManyToMany注釈に設定するPERSISTと、MERGEカスケードが理由だけでREMOVE、我々はDELETE文は、他の親レコードのために発行されますので、悪い考えであるtag私たちの場合は、しないようにpost_tag記録します。このトピックの詳細については、こちらの記事をご覧ください。

単方向の@OneToMany関連付け

マッピングの@OneToManyない単方向の関連付け@JoinColumnは、1対多ではなく、多対多のテーブルリレーションシップのように動作します。

したがって、次のエンティティマッピングがあると仮定します。

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

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

    //Constructors, getters and setters removed for brevity
}

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    @Id
    @GeneratedValue
    private Long id;

    private String review;

    //Constructors, getters and setters removed for brevity
}

Hibernateは、上記のエンティティマッピングに対して次のデータベーススキーマを想定しています。

単方向<code> @OneToMany </ code> JPAアソシエーションデータベーステーブル

すでに説明したように、単方向@OneToManyJPAマッピングは多対多の関連付けのように動作します。

リンクテーブルをカスタマイズするには、@JoinTableアノテーションを使用することもできます。

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

これで、リンクテーブルが呼び出さpost_comment_refれ、外部キー列はpost_idpostテーブルの場合は、テーブルの場合post_comment_idはになりpost_commentます。

単方向の@OneToMany関連付けは効率的ではないため、双方向の@OneToMany関連付けまたは@ManyToOneサイドのみを使用するほうがよいでしょう。チェックアウトこの記事をこのトピックの詳細については。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.