Pregunta

Cómo hacer 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 la entidad usuario :

@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

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

¿Fue útil?

Solución

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 utilizar una clave sustituta como clave principal y marcar las claves comerciales como NaturalId:

  

Aunque recomendamos el uso de   claves sustitutas como claves primarias, usted   debería tratar de identificar claves naturales   para todas las entidades. Una clave natural es un   propiedad o combinación de propiedades   eso es único y no nulo. Es   También inmutable. Mapear las propiedades de   la llave natural dentro del    elemento. Voluntad de hibernación   generar la clave única necesaria y   restricciones de nulabilidad y, como   resultado, su mapeo será más   autodocumentado.

     

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

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

El uso de esto le ahorrará muchos dolores de cabeza en el futuro, como descubrirá cuando tenga que hacer referencia / mapear con frecuencia la clave principal compuesta.

Descubrí esto de la manera difícil, y al final 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 pueda !)

Otros consejos

Para cumplir con su requisito, puede asignar su @ManyToMany como una asignación @OneToMany. De esta manera, USER_ROLE contendrá USER_ID y ROLE_ID como clave principal compuesta

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

    }

}

recuerda

  

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

Así que hemos creado un constructor JoinedUserRoleId como este para cuidarlo

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

Y finalmente Clase de 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, 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 un JoinedUserRole, 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 realizan usando @IdClass (la otra forma es usar @EmbeddedId y @Embeddable no estoy seguro de cuál está buscando) la @IdClass es la 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án sus roles de usuario y el nombre y createDate serán su ID de usuario y rol

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

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

No estoy seguro de que quede algún problema:

  • Las dos entidades que describe tienen una PK, no un par de ellas.
  • La tabla de enlaces no tiene una entidad correspondiente, está definida implícitamente 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 claves principales. También conocía la solución con la clase @Embeddable y @EmbeddedId. Pero solo quería la solución simple con las anotaciones.

Bueno, 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 combinación:

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

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

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

al menos en mi entorno

Tenga en cuenta que la diferencia es utilizar Establecer o Lista

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top