2009-07-31 25 views
18

Comment faire en sorte que la table user_roles définisse les deux colonnes (userID, roleID) en tant que clé primaire composite. devrait être facile, juste ne peut pas se souvenir/trouver.comment créer une clé primaire composite (annotation de persistance java)

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

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

Merci.

** PLUS D'INFO

Donc, il y a une troisième table user_roles (généré automatiquement par ci-dessus) qui prend userID de l'entité user et roleID de l'entité roles. Maintenant, j'ai besoin de ces deux colonnes dans la table générée (user_roles) pour être une clé primaire composite.

Répondre

22

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-moi de mentionner seulement la méthode recommandée pour le faire en veille prolongée à la place, ce qui est d'utiliser une clé de substitution comme clé primaire, et pour marquer les clés d'affaires comme de NaturalId:

Bien que nous recommandons l'utilisation des clés de substitution comme les clés primaires, vous devriez essayer d'identifier les clés naturelles pour toutes les entités. Une clé naturelle est une propriété ou une combinaison de propriétés unique et non nulle. C'est également immuable. Mappez les propriétés de la clé naturelle à l'intérieur de l'élément . Hibernate génère la clé unique nécessaire et les contraintes de nullité et, en tant que résultat , votre mappage sera plus auto-documenté.

Il est recommandé d'implémenter equals() et hashCode() pour comparer les propriétés de clé naturelle de l'entité.

Dans le code, en utilisant des annotations, ce ressemblerait à quelque chose comme ceci:

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

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

L'utilisation de ce vous permettra d'économiser beaucoup de maux de tête sur la route, que vous découvrirez quand vous avez souvent référence/mappe la clé primaire composée.

Je l'ai trouvé à la dure, et à la fin j'ai tout simplement renoncé à me battre contre Hibernate et j'ai plutôt décidé de suivre le mouvement. Je comprends parfaitement que cela pourrait ne pas être possible dans votre cas, car vous pourriez avoir affaire avec des logiciels ou des dépendances, mais je voulais juste le mentionner pour référence future. (si vous ne pouvez pas l'utiliser peut-être quelqu'un d'autre peut!)

+0

Je ne sais pas si Hibernate est goi ng pour vous donner quelque chose de plus en utilisant JPA. –

1

Merci d'avoir amélioré votre question ... et de tenir compte des suggestions.

(Désolé, il est un peu étrange que vous Postfix vos entités taos, mais ce n'est pas le point.)

Je ne suis pas sûr qu'il ya un problème gauche:

  • Le 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 leur relation ManyToMany. Si vous avez besoin d'une troisième entité, changez votre ManyToMany pour une paire de relations OneToMany et ManyToOne.
+0

mais ce n'est pas ce que je veux. ici, il semble faire la clé composite sur la classe qui n'est pas générée automatiquement. J'ai besoin d'une clé composite dans la table user_roles pour laquelle je n'ai pas de classe. la table est générée automatiquement à partir des classes user et role dao. donc si je voulais une clé composite dans par exemple userdao alors cela fonctionnerait. mais je n'ai pas besoin de ça. – b0x0rz

+0

dao postfix est l'héritage de quelqu'un d'autre. – b0x0rz

+0

"Il est également possible de mapper une relation plusieurs-à-plusieurs entre vos deux classes." Oui, j'ai déjà fait ça (en question). Maintenant, j'ai besoin pour cette table générée de ne pas être sans clé primaire. ET j'ai besoin que cette clé primaire soit une clé primaire composite de toutes les colonnes (toutes les deux) dans la table user_roles. – b0x0rz

2

clés composites sont effectuées à l'aide @IdClass (l'autre possibilité est d'utiliser EmbeddedId et @Embeddable pas sûr que vous cherchez) la @IdClass est la suivante

@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 être votre user_roles et le nom et createdate sera votre code d'utilisateur et roleId

+0

Ouais, l'import est que vous avez besoin d'un classe supplémentaire pour la combinaison de touches Je recommande d'utiliser un outil de génération automatique de mappage pour créer la couche ORM pour vous.J'utilise personnellement Netbeans –

12

Afin de répondre à vos besoins, vous pouvez mapper votre @ManyToMany comme un mappage @OneToMany. De cette façon, USER_ROLE contiendra à la fois USER_ID et ROLE_ID en tant que composé clé primaire

Je vais vous montrer comment:

@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 un assigné identifiant , ou une clé composite, l'identificateur DEVRAIT ÊTRE ATTRIBUÉ à l'instance d'objet AVANT d'appeler save().

Nous avons donc créé un constructeur JoinedUserRoleId comme celui-ci afin de prendre soin

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

Et enfin la 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); 
    } 

} 

Selon le tester, nous allons écrire les éléments suivants

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

Remarque accord au code ci-dessus NO REFERENCE à la classe JoinedUserRole.

Si vous voulez récupérer un JoinedUserRole, essayez les suivantes

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

salutations

+2

Wow, vous y mettez beaucoup de travail! +1 – Tim

1

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

Eh bien, je trouve l'illumination à travers cet article: http://www.vaannila.com/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html

et voici la magie:

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

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

cette ne génère pas de 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

Questions connexes