web-dev-qa-db-ja.com

複合主キーの作成方法(Java永続化アノテーション)

テーブル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(上記で自動生成)は、userIDエンティティからuserを受け取り、roleIDエンティティからrolesを受け取ります。ここで、生成されたテーブル(user_roles)を複合主キーにします。

18
b0x0rz

あなたが尋ねるとおりに正確に行う方法について、ここでいくつかの良い答えがすでにあります。

参考までに、代わりにHibernateでこれを行う推奨方法を説明します。これは、代理キーを主キーとして使用し、ビジネスキーをNaturalIdとしてマークすることです。

主キーとして代理キーを使用することをお勧めしますが、すべてのエンティティの自然キーを識別してみてください。自然キーは、一意でnullでないプロパティまたはプロパティの組み合わせです。また、不変です。要素内の自然キーのプロパティをマッピングします。 Hibernateは必要な一意のキーとnull可能性の制約を生成し、その結果、マッピングはより自己文書化されます。

エンティティの自然キープロパティを比較するには、equals()およびhashCode()を実装することをお勧めします。

コードでは、注釈を使用すると、次のようになります。

@Entity
public class UserRole {
  @Id
  @GeneratedValue
  private long id;

  @NaturalId
  private User user;
  @NaturalId
  private Role role;
}

これを使用すると、構成された主キーを頻繁に参照/マップする必要がある場合に気づくように、多くの頭痛の種を減らすことができます。

私はこれを難しい方法で見つけ、結局は休止状態との戦いをあきらめて、代わりに流れに行くことにしました。レガシーソフトウェアや依存関係を扱っている可能性があるため、これが可能でない場合があることを十分に理解していますが、将来の参考のために言及したいと思います。 (それを使用できない場合は、おそらく他の誰かが使用できます!)

22
Tim

要件を満たすために、@ 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()を呼び出す前に、識別子をオブジェクトインスタンスに割り当てる必要があります(SHOULD BE ASSIGNED)。

そのため、このような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();

よろしく、

12
Arthur Ronald

複合キーは@IdClassを使用して行われます(もう1つの方法は、@ 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はあなたのユーザーIDとロールIDになります

2
OpenSource

質問を改善していただきありがとうございます...そして提案を考慮に入れてください。

(申し訳ありませんが、エンティティをDaosで後置するのは少し奇妙ですが、それは重要ではありません。)

問題が残っているかどうかはわかりません。

  • 説明する2つのエンティティにはそれぞれ、ペアではなく1つのPKがあります。
  • リンクテーブルには対応するエンティティがありません。2つのエンティティとそのManyToMany関係によって暗黙的に定義されます。 3番目のエンティティが必要な場合は、OneToManyとManyToOneの関係のペアのManyToManyを変更します。
1
KLE

主キーで同じ問題がありました。また、@ Embeddableおよび@EmbeddedIdクラスを使用したソリューションも知っていました。しかし、アノテーションを使用した単純なソリューションが欲しかった。

この記事でEnlightenmentを見つけました: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;

少なくとも私の環境では

違いはSetまたはListを使用していることに注意してください

1
steveeen