31

Otra pregunta Hibernate ...: PHibernate asociación muchos-a-muchos con la misma entidad

Usando marco Anotaciones de Hibernate, que tiene una entidad User. Cada User puede tener una colección de amigos: una colección de otros User s. Sin embargo, no he podido averiguar cómo crear una asociación de muchos a muchos dentro de la clase User que consiste en una lista de User s (utilizando una tabla intermedia de usuarios y amigos).

Aquí es la clase de usuario y sus anotaciones:

@Entity 
@Table(name="tbl_users") 
public class User { 

    @Id 
    @GeneratedValue 
    @Column(name="uid") 
    private Integer uid; 

    ... 

    @ManyToMany(
      cascade={CascadeType.PERSIST, CascadeType.MERGE}, 
      targetEntity=org.beans.User.class 
    ) 
    @JoinTable(
      name="tbl_friends", 
      [email protected](name="personId"), 
      [email protected](name="friendId") 
    ) 
    private List<User> friends; 
} 

El usuario-amigo tabla de asignación sólo tiene dos columnas, las cuales son claves ajenas a la columna de la uid de la mesa tbl_users. Las dos columnas son personId (que debe correlacionarse con el usuario actual) y friendId (que especifica la identificación del amigo del usuario actual).

El problema es que el campo "amigos" sigue siendo nulo, a pesar de que he rellenado previamente la tabla de amigos de modo que todos los usuarios del sistema sean amigos de todos los demás usuarios. Incluso he intentado cambiar la relación a @OneToMany, y todavía sale nulo (aunque la salida de depuración de Hibernate muestra una consulta SELECT * FROM tbl_friends WHERE personId = ? AND friendId = ?, pero nada más).

¿Alguna idea de cómo llenar esta lista? ¡Gracias!

Respuesta

47

@ManyToMany para uno mismo es bastante confuso porque la forma en que normalmente se modelaría esto difiere del modo "Hibernate". Tu problema es que te estás perdiendo otra colección.

Piénselo de esta manera: si mapea "autor"/"libro" como muchos-a-muchos, necesita la colección "autores" en la colección Libro y "libros" en Autor. En este caso, su entidad "Usuario" representa ambos extremos de una relación; por lo que necesita "mis amigos" y "amigo" de colecciones:

@ManyToMany 
@JoinTable(name="tbl_friends", 
[email protected](name="personId"), 
[email protected](name="friendId") 
) 
private List<User> friends; 

@ManyToMany 
@JoinTable(name="tbl_friends", 
[email protected](name="friendId"), 
[email protected](name="personId") 
) 
private List<User> friendOf; 

Todavía se puede utilizar la misma tabla de asociación, pero tenga en cuenta que unen las columnas/inverseJon están intercambiados en colecciones.

Las colecciones "friends" y "friendOf" pueden coincidir o no (dependiendo de si tu "amistad" es siempre mutua) y no tienes que exponerlas de esta manera en tu API, por supuesto, pero eso es la forma de mapearlo en Hibernate.

+0

Te estaba esperando Había venido a mi rescate por tercera vez :) Funcionó a la perfección, y tu explicación aclara mi comprensión de ER significativamente. ¡Una vez mas, muchas gracias! :) – Magsol

+1

¿Puedo combinar amigos y amigo? Y si no, entonces necesito ayuda con jsonmanagedreference y jsonbackreference en una entidad en esta pregunta: http://stackoverflow.com/questions/24563066/hibernate-user-and-friends-jsonreference – dikkini

+0

Estoy usando Hibernate 5 y una colección es suficiente, usar dos crea filas duplicadas en tbl_friends. – mdziob

0

En realidad es muy simple, y podría lograrse siguiendo decir que has entidad siguiente

public class Human { 
    int id; 
    short age; 
    String name; 
    List<Human> relatives; 



    public int getId() { 
     return id; 
    } 
    public void setId(int id) { 
     this.id = id; 
    } 
    public short getAge() { 
     return age; 
    } 
    public void setAge(short age) { 
     this.age = age; 
    } 
    public String getName() { 
     return name; 
    } 
    public void setName(String name) { 
     this.name = name; 
    } 
    public List<Human> getRelatives() { 
     return relatives; 
    } 
    public void setRelatives(List<Human> relatives) { 
     this.relatives = relatives; 
    } 

    public void addRelative(Human relative){ 
     if(relatives == null) 
      relatives = new ArrayList<Human>(); 
     relatives.add(relative); 
    } 




} 

HBM para la misma:

<hibernate-mapping> 
    <class name="org.know.july31.hb.Human" table="Human"> 
     <id name="id" type="java.lang.Integer"> 
      <column name="H_ID" /> 
      <generator class="increment" /> 
     </id> 
     <property name="age" type="short"> 
      <column name="age" /> 
     </property> 
     <property name="name" type="string"> 
      <column name="NAME" length="200"/> 
     </property> 
     <list name="relatives" table="relatives" cascade="all"> 
     <key column="H_ID"/> 
     <index column="U_ID"/> 
     <many-to-many class="org.know.july31.hb.Human" column="relation"/> 
     </list> 
    </class> 
</hibernate-mapping> 

Y prueba de caso

import org.junit.Test; 
import org.know.common.HBUtil; 
import org.know.july31.hb.Human; 

public class SimpleTest { 

    @Test 
    public void test() { 
     Human h1 = new Human(); 
     short s = 23; 
     h1.setAge(s); 
     h1.setName("Ratnesh Kumar singh"); 
     Human h2 = new Human(); 
     h2.setAge(s); 
     h2.setName("Praveen Kumar singh"); 
     h1.addRelative(h2); 
     Human h3 = new Human(); 
     h3.setAge(s); 
     h3.setName("Sumit Kumar singh"); 
     h2.addRelative(h3); 
     Human dk = new Human(); 
     dk.setAge(s); 
     dk.setName("D Kumar singh"); 
     h3.addRelative(dk); 
     HBUtil.getSessionFactory().getCurrentSession().beginTransaction(); 
     HBUtil.getSessionFactory().getCurrentSession().save(h1); 
     HBUtil.getSessionFactory().getCurrentSession().getTransaction().commit(); 
     HBUtil.getSessionFactory().getCurrentSession().beginTransaction(); 
     h1 = (Human)HBUtil.getSessionFactory().getCurrentSession().load(Human.class, 1); 
     System.out.println(h1.getRelatives().get(0).getName()); 

     HBUtil.shutdown(); 
    } 

} 
Cuestiones relacionadas