comment faire une clé primaire composite (java persistance d'annotation)

comment faire pour que la table user_roles définisse les deux colonnes (userID, roleID) comme une clé primaire composite. devrait être facile, juste Je ne me souviens pas/trouver.

user entité:

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

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles")
public List<UserDAO> getUsers() {
    return users;
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getRoleID() {
    return roleID;
}

je vous Remercie.

** MORE INFO

il y a donc une troisième table user_roles (généré automatiquement par au-dessus)userIDuser entity roleIDroles entité. Maintenant j'ai besoin de ces deux colonnes dans la table générée (user_roles) pour une clé primaire composite.

18
demandé sur nyxz 2009-07-31 15:36:16

5 réponses

Vous avez déjà eu quelques bonnes réponses ici sur la façon de faire exactement ce que vous demandez..

pour référence laissez - moi juste mentionner la façon recommandée de faire ceci en hibernation à la place, qui est d'utiliser une clé de substitution comme clé primaire, et de marquer des clés d'affaires comme Naturalid's:

bien que nous recommandons l'utilisation de les clés de substitution comme clés primaires, vous devrait essayer d'identifier les clés naturelles pour toutes les entités. Une clé naturelle est un propriété ou combinaison de propriétés qui est unique et non-null. Il est aussi immuable. Carte les propriétés de la clé naturelle à l'intérieur de la élément. Hibernate générer les clés uniques et les contraintes de nullité et, en tant que résultat, votre cartographie sera plus l'auto-documentation.

il est recommandé d'implémenter equals() et hashCode() pour comparer les naturelles principales propriétés de l'entité.

dans le code, en utilisant des annotations, cela ressemblerait à quelque chose comme ceci:

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

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

utiliser ceci vous évitera beaucoup de maux de tête à l'avenir, car vous découvrirez quand vous devrez fréquemment faire référence à la clé primaire composée.

j'ai découvert cela à la dure, et à la fin juste abandonné la lutte contre hibernation et à la place décidé d'aller avec le flux. Je comprends parfaitement que cela ne soit peut-être pas possible dans votre cas, car vous avez peut-être affaire à un ancien logiciel ou à des dépendances, mais je voulais juste le mentionner pour l'avenir. référence. (si vous ne pouvez pas l'utiliser peut-être quelqu'un d'autre peut!)

22
répondu Tim 2009-08-09 21:29:22

afin de répondre à vos exigences, vous pouvez cartographier votre @ManyToMany comme un @OneToMany mapping. De cette façon, USER_ROLE contiendra USER_ID et ROLE_ID comme clé primaire composée

je vais vous montrer comment faire:

@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;
        }

    }

}

souvenir

si un objet a une affectation identificateur, ou une clé composite, l'identificateur doit être attribué à la instance de l'objet avant d'appeler save ().

nous avons Donc créé une JoinedUserRoleId constructeur comme celui-ci, afin de prendre soin d'elle

public JoinedUserRoleId(User user, Role role) {
    this.user = user;
    this.role = role;
}

et enfin classe de rôle

@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);
    }

}

D'après test it, écrivons ce qui suit

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();

avis selon le code ci-dessus aucune référence à la classe JoinedUserRole.

si vous voulez récupérer un JoinedUserRole, essayez

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();

cordialement,

12
répondu Arthur Ronald 2009-08-10 18:31:03

Les clés composites sont faites en utilisant @IdClass (l'autre moyen est d'utiliser @EmbeddedId et @Embeddable pas sûr de celui que vous recherchez) le @IdClass est comme suit

@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();
    }
}

ma catégorie ici sera votre user_roles et le nom et createDate sera votre userid et roleid

2
répondu OpenSource 2009-08-06 21:29:39

Merci d'améliorer votre question ... et en tenant compte des suggestions.

(Désolé, C'est un peu étrange que vous postfix votre entités avec Daos, mais ce n'est pas le point.)

je ne suis pas sûr qu'il y est tout le problème de la gauche :

  • les deux entités que vous décrivez ont chacune un PK, pas une paire de.
  • la table de liens n'a pas d'entité correspondante, elle est définie implicitement par les deux entités et leurs nombreuses relations. Si vous besoin d'une troisième entité, changer votre ManyToMany pour une paire de OneToMany et ManyToOne relations.
1
répondu KLE 2009-08-09 08:42:41

j'ai eu le même Problème avec les clés primaires. Je connaissais aussi la solution avec la classe @Embeddable et @EmbeddedId. Mais je voulais juste la solution simple avec les annotations.

eh Bien j'ai trouvé l'illumination par le biais de cet article: http://www.vaannila.com/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html

et ici, c'est la magie:

cela génère une clé primaire sur la jointure tableau:

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="classA_classB")
private Set<ClassA> classesA;

c'dosn pas générer une clé primaire sur la table de jointure:

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="classA_classB")
private List<ClassA> classesA;

au moins dans mon environnement

notez que la différence utilise Set ou Liste

1
répondu steveeen 2013-02-10 15:47:40