これは非常に一般的な質問なので、この回答は私がブログに書いたこの記事に基づいています。
1対多
1対多のテーブルの関係は次のようになります。
リレーショナルデータベースシステムでは、1対多のテーブルリレーションシップは、親テーブル行のForeign Key
を参照する子の列に基づいて2つのテーブルをリンクしますPrimary Key
。
上記の表の図では、post_id
列post_comment
テーブルが有しているForeign Key
との関係post
テーブルID Primary Key
カラム:
ALTER TABLE
post_comment
ADD CONSTRAINT
fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
@ManyToOneアノテーション
1対多のテーブルの関係をマッピングする最良の方法は、@ManyToOne
アノテーションを使用することです。
この場合、子エンティティは、注釈を使用して外部キー列をPostComment
マップします。post_id
@ManyToOne
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
@Id
@GeneratedValue
private Long id;
private String review;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
}
JPA @OneToMany
アノテーションの使用
@OneToMany
アノテーションを使用するオプションがあるからといって、これがすべての1対多のデータベース関係のデフォルトオプションであることを意味するわけではありません。コレクションの問題は、子レコードの数がかなり限られている場合にのみ使用できることです。
@OneToMany
関連付けをマッピングする最良の方法は、@ManyToOne
すべてのエンティティの状態変更を伝播する側に依存することです。
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
//Constructors, getters and setters removed for brevity
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
}
親エンティティは、双方向の関連付けの両側を同期するために使用されるPost
2つのユーティリティメソッド(addComment
およびなどremoveComment
)を備えています。双方向の関連付けを使用しているときは常に、これらのメソッドを常に提供する必要があります。そうしないと、非常に微妙な状態伝播の問題が発生するおそれがあります。
単方向の@OneToMany
関連付けは、を使用し@ManyToOne
たり、双方向の@OneToMany
関連付けよりも効率が悪いため、避けてください。
@OneToMany
JPAおよびHibernateとの関係をマッピングする最良の方法について詳しくは、こちらの記事をご覧ください。
1対1
1対1のテーブルの関係は次のようになります。
リレーショナルデータベースシステムでは、1対1のテーブルリレーションシップは、親テーブル行の参照Primary Key
でもある子の列に基づいて2つのテーブルをリンクします。Foreign Key
Primary Key
したがって、子テーブルPrimary Key
は親テーブルと共有していると言えます。
上記の表の図では、id
列post_details
テーブルも有しているForeign Key
との関係post
テーブルのid
Primary Key
列を:
ALTER TABLE
post_details
ADD CONSTRAINT
fk_post_details_id
FOREIGN KEY (id) REFERENCES post
JPAを使用し@OneToOne
て@MapsId
注釈
@OneToOne
関係をマッピングする最良の方法は、を使用すること@MapsId
です。この方法ではPostDetails
、Post
エンティティ識別子を使用してエンティティをいつでもフェッチできるため、双方向の関連付けも必要ありません。
マッピングは次のようになります。
[code language = "java"] @Entity(name = "PostDetails")@Table(name = "post_details")パブリッククラスPostDetails {
@Id
private Long id;
@Column(name = "created_on")
private Date createdOn;
@Column(name = "created_by")
private String createdBy;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
@JoinColumn(name = "id")
private Post post;
public PostDetails() {}
public PostDetails(String createdBy) {
createdOn = new Date();
this.createdBy = createdBy;
}
//Getters and setters omitted for brevity
} [/コード]
このように、id
プロパティは主キーと外部キーの両方として機能します。識別子に関連付けの識別子が入力されているため、@Id
列で@GeneratedValue
アノテーションが使用されなくなっていることがわかりpost
ます。
@OneToOne
JPAおよびHibernateとの関係をマッピングする最良の方法について詳しくは、こちらの記事をご覧ください。
多対多
多対多のテーブルの関係は次のようになります。
リレーショナルデータベースシステムでは、多対多のテーブルリレーションシップは、2つの親テーブルのForeign Key
列を参照する2つの列を含む子テーブルを介して2つの親テーブルをリンクしますPrimary Key
。
上記の表の図では、post_id
列post_tag
テーブルも有しているForeign Key
との関係post
テーブルID Primary Key
カラム:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post
そして、tag_id
列post_tag
の表では、持っているForeign Key
との関係tag
テーブルのid Primary Key
列を:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag
JPA @ManyToMany
マッピングの使用
これはmany-to-many
、JPAおよびHibernateとテーブルの関係をマップする方法です。
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "post_tag",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private Set<Tag> tags = new HashSet<>();
//Getters and setters ommitted for brevity
public void addTag(Tag tag) {
tags.add(tag);
tag.getPosts().add(this);
}
public void removeTag(Tag tag) {
tags.remove(tag);
tag.getPosts().remove(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Post)) return false;
return id != null && id.equals(((Post) o).getId());
}
@Override
public int hashCode() {
return 31;
}
}
@Entity(name = "Tag")
@Table(name = "tag")
public class Tag {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String name;
@ManyToMany(mappedBy = "tags")
private Set<Post> posts = new HashSet<>();
//Getters and setters ommitted for brevity
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tag tag = (Tag) o;
return Objects.equals(name, tag.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
- エンティティの
tags
関連付けPost
は、PERSIST
およびMERGE
カスケードタイプのみを定義します。この記事で説明したように、REMOVE
エンティティの状態遷移は@ManyToMany
、最終的には関連付けの両側をワイプするチェーンの削除をトリガーする可能性があるため、JPAの関連付けには意味がありません。
- この記事で説明するように、双方向の関連付けを使用する場合、関連付けの両側が確実に同期するように、追加/削除ユーティリティのメソッドは必須です。
Post
それがどんなユニークなビジネスキーを欠いので、エンティティは、平等のためのエンティティの識別子を使用しています。この記事で説明されているように、エンティティ識別子は、すべてのエンティティの状態遷移で一貫性が保たれていることを確認する限り、同等に使用できます。
Tag
エンティティは、Hibernate固有のでマークされたユニークなビジネスキーがある@NaturalId
注釈を。その場合、一意のビジネスキーが同等性チェックの最適な候補になります。
- エンティティ
mappedBy
のposts
関連付けの属性は、Tag
この双方向の関係で、Post
エンティティが関連付けを所有していることを示します。これが必要なのは、関係を所有できるのは片側だけであり、変更はこの特定の側からのみデータベースに伝搬されるためです。
Set
Aを使用するものとして、好ましいことであるList
と@ManyToMany
効率が低いです。
@ManyToMany
JPAおよびHibernateとの関係をマッピングする最良の方法について詳しくは、こちらの記事をご覧ください。