cómo hacer una clave primaria compuesta (anotación de persistencia java)

Cómo hacerlo para que la tabla user_roles defina las dos columnas (userID, roleID) como una clave primaria compuesta. debería ser fácil, simplemente no puedo recordar / encontrar.

En entidad de user :

 @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "user_roles") public List getRoles() { return roles; } @Id @GeneratedValue(strategy = GenerationType.AUTO) public Integer getUserID() { return userID; } 

En la entidad de roles :

 @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "user_roles") public List getUsers() { return users; } @Id @GeneratedValue(strategy = GenerationType.AUTO) public Integer getRoleID() { return roleID; } 

Gracias.

** MÁS INFORMACIÓN

Entonces, hay una tercera tabla user_roles (autogenerada por arriba) que toma el userID de user entidad de user y el roleID de la entidad de roles . Ahora necesito esas dos columnas en la tabla generada ( user_roles ) para ser una clave primaria compuesta.

Ya has tenido algunas buenas respuestas aquí sobre cómo hacer exactamente lo que pides …

Como referencia, permítanme mencionar la forma recomendada de hacer esto en Hibernate, que es usar una clave sustituta como clave principal, y marcar las claves comerciales como NaturalId:

Aunque recomendamos el uso de claves sustitutivas como claves principales, debe intentar identificar las claves naturales para todas las entidades. Una clave natural es una propiedad o combinación de propiedades que es única y no nula. También es inmutable. Mapa de las propiedades de la clave natural dentro del elemento. Hibernate generará las restricciones clave y de nulabilidad únicas necesarias y, como resultado, su mapeo será más autodocumentado.

Se recomienda que implemente equals () y hashCode () para comparar las propiedades clave naturales de la entidad.

En el código, usando anotaciones, esto se vería así:

 @Entity public class UserRole { @Id @GeneratedValue private long id; @NaturalId private User user; @NaturalId private Role role; } 

Usar esto le ahorrará muchos dolores de cabeza en el camino, como descubrirá cuando con frecuencia tiene que hacer referencia / asignar la clave primaria compuesta.

Descubrí esto por el camino difícil, y al final simplemente dejé de luchar contra Hibernate y decidí seguir con la stream. Entiendo completamente que esto podría no ser posible en su caso, ya que podría estar lidiando con software heredado o dependencias, pero solo quería mencionarlo para referencia futura. (¡ Si no puedes usarlo, tal vez alguien más pueda !)

Para cumplir con sus requisitos, puede asignar su @ManyToMany como un mapeo @OneToMany. De esta forma, USER_ROLE contendrá tanto USER_ID como ROLE_ID como clave primaria compuesta

Yo te mostraré cómo:

 @Entity @Table(name="USER") public class User { @Id @GeneratedValue private Integer id; @OneToMany(cascade=CascadeType.ALL, mappedBy="joinedUserRoleId.user") private List joinedUserRoleList = new ArrayList(); // 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; } } } 

recuerda

Si un objeto tiene un identificador asignado, o una clave compuesta, el identificador DEBE SER ASIGNADO a la instancia del objeto ANTES de llamar a save ().

Así que hemos creado un constructor JoinUserRoleId como este para poder cuidarlo

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

Y finalmente la clase de roles

 @Entity @Table(name="ROLE") public class Role { @Id @GeneratedValue private Integer id; @OneToMany(cascade=CascadeType.ALL, mappedBy="JoinedUserRoleId.role") private List joinedUserRoleList = new ArrayList(); // 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); } } 

Según lo prueban, escribamos lo siguiente

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

Aviso según el código anterior SIN REFERENCIA a la clase JoinedUserRole.

Si desea recuperar una ActionsUserRole, intente lo siguiente

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

Saludos,

Las claves compuestas se hacen usando @IdClass (de la otra manera está usando @EmbeddedId y @Embeddable no está seguro de cuál está buscando) @IdClass es el siguiente

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

mi categoría aquí será su user_roles y el nombre y createDate serán su ID de usuario y roleid

Gracias por mejorar su pregunta … y teniendo en cuenta las sugerencias.

(Lo siento, es un poco extraño que postfixes tus entidades con Daos, pero no es el punto).

No estoy seguro de que quede algún problema:

  • Las dos entidades que describes tienen un PK, no un par de.
  • La tabla de enlace no tiene entidad correspondiente, está implícitamente definida por las dos entidades y su relación ManyToMany. Si necesita una tercera Entidad, cambie su ManyToMany por un par de relaciones OneToMany y ManyToOne.

Tuve el mismo problema con las teclas principales. También conocía la solución con @Embeddable y @EmbeddedId Class. Pero solo quería la solución simple con las anotaciones.

Bien, encontré la iluminación a través de este artículo: http://www.vaannila.com/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html

y aquí está la magia:

esto genera una clave principal en la tabla de unión:

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

esto no genera una clave principal en la tabla de unión:

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

al menos en mi entorno

Tenga en cuenta que la diferencia es usar Set o List