2009-07-31 27 views
18

Cómo hacer que la tabla user_roles defina las dos columnas (ID de usuario, ID de función) como una clave primaria compuesta. debería ser fácil, simplemente no puedo recordar/encontrar.cómo crear una clave primaria compuesta (anotación de persistencia java)

En user entidad:

@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 entidad:

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

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

Gracias.

** MÁS INFORMACIÓN

así que hay una tercera tabla user_roles (auto generada por arriba) que se lleva a userID de user entidad y roleID de roles entidad. Ahora necesito que esas dos columnas en la tabla generada (user_roles) sean una clave primaria compuesta.

Respuesta

22

usted ya ha tenido algunas buenas respuestas aquí en la forma de hacer exactamente lo que le preguntas ..

como referencia permítanme mencionar la forma recomendada de hacer esto en hibernación en su lugar, el cual es el uso de una clave sustituta como clave primaria, y para marcar las teclas de negocios como NaturalId de:

Aunque se recomienda el uso de claves suplentes como claves primarias, que debe Intente identificar las claves naturales para todas las entidades. Una clave natural es una propiedad o una combinación de propiedades que es única y no nula. Es también inmutable. Asigne las propiedades de la clave natural dentro del elemento . Hibernate dará generará la clave única necesaria y las restricciones de nulabilidad y, como un resultado , su asignación será más auto-documentada.

Se recomienda implementar equals() y hashCode() para comparar las propiedades de la clave natural de la entidad.

En el código, usando anotaciones, esto sería algo como esto:

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

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

El uso de este le ahorrará muchos dolores de cabeza en el camino, como podrá saber cuándo tiene que con frecuencia referencia/mapa de la clave primaria compuesta.

Descubrí esto por las malas, y al final simplemente dejé de luchar contra Hibernate y en su lugar decidí seguir la corriente. 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 puede usarlo tal vez alguien más puede!)

+0

No sé si Hibernate es Goi. ng para darte algo más usando JPA. –

1

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

(Lo siento, es un poco extraño que Postfix sus entidades con dao, pero no es el punto.)

No estoy seguro de que hay algún problema a la izquierda:

  • El 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.
+0

pero eso no es lo que quiero. aquí parece hacer que la clave compuesta en la clase no se genere automáticamente. Necesito una clave compuesta en la tabla user_roles para la cual no tengo CUALQUIER clase. la tabla se genera automáticamente a partir de las clases dao de usuario y rol. así que si quisiera una clave compuesta en, por ejemplo, userdao, entonces esto funcionaría. pero no necesito/quiero eso. – b0x0rz

+0

dao postfix es heredado de otra persona. – b0x0rz

+0

"También es posible mapear una relación de muchos a muchos entre sus dos clases". Sí, ya lo hice (en cuestión). Ahora necesito que esa tabla GENERADA no esté sin una clave primaria. Y necesito que la clave primaria sea una clave primaria compuesta de todas (ambas) columnas en la tabla user_roles. – b0x0rz

2

claves compuestas se realizaron utilizando @IdClass (la otra forma es utilizar @EmbeddedId y @Embeddable no está seguro de lo que uno está buscando) la @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í se sea ​​su user_roles y el nombre y CreateDate será su ID de usuario y roleid

+0

Sí, lo importante es que necesita una clase adicional para la clave combinada. Recomiendo usar una herramienta de generación automática de mapas para crear la capa ORM para usted. Personalmente uso Netbeans. –

12

Para cumplir con sus requisitos, puede asignar su @ManyToMany como una asignación @OneToMany. De esta manera, user_role contendrá tanto USER_ID y ROLE_ID como compuesto principal clave

voy a mostrar cómo:

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

    } 

} 

recordar

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

por ello hemos creado un constructor JoinedUserRoleId como éste con el fin de cuidar de él

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

Y por último la clase Rol

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

} 

De acuerdo con probarlo, vamos a escribir la 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(); 

notas concordar al código anterior NINGUNA REFERENCIA a la clase JoinedUserRole.

Si desea recuperar un JoinedUserRole, pruebe los siguientes

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

respecto,

+2

¡Wow, has puesto mucho trabajo en esto! +1 – Tim

1

Yo tenía el mismo problema con las claves primarias. También conocía la solución con las clases @Embeddable y @EmbeddedId. Pero solo quería la solución simple con las anotaciones.

Bueno me encontré con 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<ClassA> classesA; 

este no puede generar una clave principal en la tabla de unión:

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

al menos en mi entorno

Tenga en cuenta que la diferencia está utilizando Establecer o Lista

Cuestiones relacionadas