複合主キーの作成方法(Java永続性注釈)
-
06-07-2019 - |
質問
テーブルuser_rolesが2つの列(userID、roleID)を複合主キーとして定義するようにする方法。簡単である必要があります。思い出すことも見つけることもできません。
user
エンティティ:
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles")
public List<RoleDAO> getRoles() {
return roles;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getUserID() {
return userID;
}
roles
エンティティ:
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles")
public List<UserDAO> getUsers() {
return users;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getRoleID() {
return roleID;
}
ありがとう。
**詳細
3番目のテーブル user_roles
(上記で自動生成)があり、 user
エンティティと roleID
roles
エンティティからのcode>。ここで、生成されたテーブル( user_roles
)のこれらの2つの列を複合主キーにする必要があります。
解決
質問通りに行う方法については、すでにいくつかの良い回答があります。
参考のために、代わりにHibernateでこれを行う推奨方法、つまりサロゲートキーをプライマリキーとして使用し、ビジネスキーをNaturalIdとしてマークする方法について説明します。
の使用をお勧めしますが 主キーとしての代理キー、あなた 自然なキーを識別しようとする必要があります すべてのエンティティに対して。自然な鍵は プロパティまたはプロパティの組み合わせ それは一意であり、nullではありません。それは また不変です。のプロパティをマップする 内部の自然な鍵 素子。 Hibernateは 必要な一意のキーを生成し、 nullability制約、および 結果、マッピングはより多くなります 自己文書化。
実装することをお勧めします equals()とhashCode()で比較します エンティティの自然キープロパティ。
注釈を使用したコードでは、これは次のようになります。
@Entity
public class UserRole {
@Id
@GeneratedValue
private long id;
@NaturalId
private User user;
@NaturalId
private Role role;
}
これを使用すると、作成された主キーを頻繁に参照/マッピングする必要があるときにわかるように、今後の多くの頭痛の種を節約できます。
これは難しい方法であることがわかり、最終的にはHibernateとの戦いをあきらめ、代わりにフローを使用することにしました。レガシーソフトウェアや依存関係を扱っている可能性があるため、これはあなたのケースでは不可能かもしれないことを完全に理解していますが、今後の参考のために言及したいだけです。 (使用できない場合は、おそらく他の誰かが使用できる!)
他のヒント
要件を満たすために、@ ManyToManyを@OneToManyマッピングとしてマッピングできます。 これにより、USER_ROLEには、複合主キーとしてUSER_IDとROLE_IDの両方が含まれます
次の方法を説明します:
@Entity
@Table(name="USER")
public class User {
@Id
@GeneratedValue
private Integer id;
@OneToMany(cascade=CascadeType.ALL, mappedBy="joinedUserRoleId.user")
private List<JoinedUserRole> joinedUserRoleList = new ArrayList<JoinedUserRole>();
// no-arg required constructor
public User() {}
public User(Integer id) {
this.id = id;
}
// addRole sets up bidirectional relationship
public void addRole(Role role) {
// Notice a JoinedUserRole object
JoinedUserRole joinedUserRole = new JoinedUserRole(new JoinedUserRole.JoinedUserRoleId(this, role));
joinedUserRole.setUser(this);
joinedUserRole.setRole(role);
joinedUserRoleList.add(joinedUserRole);
}
}
@Entity
@Table(name="USER_ROLE")
public class JoinedUserRole {
public JoinedUserRole() {}
public JoinedUserRole(JoinedUserRoleId joinedUserRoleId) {
this.joinedUserRoleId = joinedUserRoleId;
}
@ManyToOne
@JoinColumn(name="USER_ID", insertable=false, updatable=false)
private User user;
@ManyToOne
@JoinColumn(name="ROLE_ID", insertable=false, updatable=false)
private Role role;
@EmbeddedId
// Implemented as static class - see bellow
private JoinedUserRoleId joinedUserRoleId;
// required because JoinedUserRole contains composite id
@Embeddable
public static class JoinedUserRoleId implements Serializable {
@ManyToOne
@JoinColumn(name="USER_ID")
private User user;
@ManyToOne
@JoinColumn(name="ROLE_ID")
private Role role;
// required no arg constructor
public JoinedUserRoleId() {}
public JoinedUserRoleId(User user, Role role) {
this.user = user;
this.role = role;
}
public JoinedUserRoleId(Integer userId, Integer roleId) {
this(new User(userId), new Role(roleId));
}
@Override
public boolean equals(Object instance) {
if (instance == null)
return false;
if (!(instance instanceof JoinedUserRoleId))
return false;
final JoinedUserRoleId other = (JoinedUserRoleId) instance;
if (!(user.getId().equals(other.getUser().getId())))
return false;
if (!(role.getId().equals(other.getRole().getId())))
return false;
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 47 * hash + (this.user != null ? this.user.hashCode() : 0);
hash = 47 * hash + (this.role != null ? this.role.hashCode() : 0);
return hash;
}
}
}
記憶する
オブジェクトに割り当てられている場合 識別子、または複合キー、 識別子は save()を呼び出す前のオブジェクトインスタンス。
それで、これを処理するために、このようなJoinedUserRoleIdコンストラクターを作成しました
public JoinedUserRoleId(User user, Role role) {
this.user = user;
this.role = role;
}
そして最後にRoleクラス
@Entity
@Table(name="ROLE")
public class Role {
@Id
@GeneratedValue
private Integer id;
@OneToMany(cascade=CascadeType.ALL, mappedBy="JoinedUserRoleId.role")
private List<JoinedUserRole> joinedUserRoleList = new ArrayList<JoinedUserRole>();
// no-arg required constructor
public Role() {}
public Role(Integer id) {
this.id = id;
}
// addUser sets up bidirectional relationship
public void addUser(User user) {
// Notice a JoinedUserRole object
JoinedUserRole joinedUserRole = new JoinedUserRole(new JoinedUserRole.JoinedUserRoleId(user, this));
joinedUserRole.setUser(user);
joinedUserRole.setRole(this);
joinedUserRoleList.add(joinedUserRole);
}
}
テストに従って、次のように書きましょう
User user = new User();
Role role = new Role();
// code in order to save a User and a Role
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Serializable userId = session.save(user);
Serializable roleId = session.save(role);
session.getTransaction().commit();
session.clear();
session.close();
// code in order to set up bidirectional relationship
Session anotherSession = HibernateUtil.getSessionFactory().openSession();
anotherSession.beginTransaction();
User savedUser = (User) anotherSession.load(User.class, userId);
Role savedRole = (Role) anotherSession.load(Role.class, roleId);
// Automatic dirty checking
// It will set up bidirectional relationship
savedUser.addRole(savedRole);
anotherSession.getTransaction().commit();
anotherSession.clear();
anotherSession.close();
上記のコードによると、JoinedUserRoleクラスへの参照はありません。
JoinedUserRoleを取得する場合は、次を試してください
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Integer userId;
Integer roleId;
// Lets say you have set up both userId and roleId
JoinedUserRole joinedUserRole = (JoinedUserRole) session.get(JoinedUserRole.class, new JoinedUserRole.JoinedUserRoleId(userId, roleId));
// some operations
session.getTransaction().commit();
session.clear();
session.close();
よろしく、
複合キーは@IdClassを使用して実行されます(他の方法は、@ EmbeddedIdと@Embeddableを使用して、どちらを探しているかわからない)@IdClassは次のとおりです
@Entity
@IdClass(CategoryPK.class)
public class Category {
@Id
protected String name;
@Id
protected Date createDate;
}
public class CategoryPK implements Serializable {
String name;
Date createDate;
public boolean equals(object other) {
//implement a equals that the PP can use to determine
//how the CategoryPK object can be identified.
}
public int hashCode(){
return Super.hashCode();
}
}
ここでの私のカテゴリはあなたのuser_rolesであり、名前とcreateDateはあなたのuseridとroleidです
質問を改善していただきありがとうございます...そして提案を考慮に入れてください。
(申し訳ありませんが、エンティティをDaosで後置するのは少し奇妙ですが、それはポイントではありません。)
問題が残っているかどうかわからない:
- 説明する2つのエンティティにはそれぞれ、ペアではなく1つのPKがあります。
- リンクテーブルには対応するエンティティがありません。2つのエンティティとそのManyToMany関係によって暗黙的に定義されます。 3番目のエンティティが必要な場合は、OneToManyとManyToOneのリレーションシップのペアのManyToManyを変更します。
主キーでも同じ問題が発生しました。 また、@ Embeddableクラスと@EmbeddedIdクラスを使用したソリューションも知っていました。しかし、私は注釈付きのシンプルなソリューションが欲しかった。
この記事で啓発を見つけました: http:// www .vaannila.com / hibernate / hibernate-example / hibernate-mapping-many-to-many-using-annotations-1.html
そしてここに魔法があります:
これにより、結合テーブルに主キーが生成されます。
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="classA_classB")
private Set<ClassA> classesA;
これは、結合テーブルに主キーを生成しません:
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="classA_classB")
private List<ClassA> classesA;
少なくとも私の環境では
違いは、設定またはリスト
を使用していることに注意してください