web-dev-qa-db-ja.com

JPA「@JoinTable」アノテーション

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

125
kostas trichas

EDIT 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;

}

他の解決策は、3番目のテーブルを使用することです。 Project_Tasks、およびプロジェクトとタスク間の関係をそのテーブルに保存します。

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

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

Project entity:

@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:

@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 Diagram 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 Diagram 2

最後に、多対多の関連付けのスキーマを作成する場合は、結合テーブルを使用することが唯一の解決策です。

305
Behrang

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

また、多くのサイドのテーブルに外部キーを追加したくないため、OneToMany(通常は単方向)アソシエーションにも使用されます。

説明と例については、 hibernate documentation で@JoinTableを検索してください。

13
JB Nizet

また、エンティティが異なるタイプの親との複数の親/子関係の子になる可能性がある場合は、@JoinTableを使用する方がクリーンです。 Behrangの例をフォローアップするために、タスクがProject、Person、Department、Study、およびProcessの子になることができると想像してください。

taskテーブルには、5つのnullable外部キーフィールドが必要ですか?私はそうは思いません...

13
HDave

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

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 |
|_________|_________|
|         |         |
|_________|_________|
4
slal