relacion muchos many java hibernate java-ee entity-relationship hibernate-annotations

java - many - Hibernate recursivo asociación muchos a muchos con la misma entidad



relacion muchos a muchos hibernate annotations (3)

@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; entonces necesitas colecciones de "mis amigos" y "amigo de":

@ManyToMany @JoinTable(name="tbl_friends", joinColumns=@JoinColumn(name="personId"), inverseJoinColumns=@JoinColumn(name="friendId") ) private List<User> friends; @ManyToMany @JoinTable(name="tbl_friends", joinColumns=@JoinColumn(name="friendId"), inverseJoinColumns=@JoinColumn(name="personId") ) private List<User> friendOf;

Aún puede usar la misma tabla de asociación, pero tenga en cuenta que las columnas join / inverseJon se intercambian en las 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 esa es la manera de mapear en Hibernate.

Otra pregunta de Hibernate ...: P

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

Aquí está 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", joinColumns=@JoinColumn(name="personId"), inverseJoinColumns=@JoinColumn(name="friendId") ) private List<User> friends; }

La tabla de asignación de usuario amigo solo tiene dos columnas, ambas claves externas a la columna uid de la tabla tbl_users . Las dos columnas son personId (que debe personId al usuario actual) y friendId (que especifica el id del amigo del usuario actual).

El problema es que el campo de "amigos" sigue siendo nulo, aunque 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 con @OneToMany , y todavía sale nulo (aunque la salida de depuración de Hibernate muestra SELECT * FROM tbl_friends WHERE personId = ? AND friendId = ? Query, pero nada más).

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


En realidad es muy simple y podría lograrse al seguir diciendo que tienes la siguiente entidad

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 por lo mismo:

<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 caso de prueba

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


La respuesta aceptada parece demasiado complicada con las anotaciones @JoinTable . Una implementación un poco más simple solo necesita un mappedBy . El uso de mappedBy indica la Entity o propiedad propietaria, que probablemente debería ser las referencesTo ya que se considerarían los "amigos". Una relación ManyToMany puede crear un gráfico muy complicado. El uso de mappedBy hace que el código sea así:

@Entity public class Recursion { @Id @GeneratedValue private Integer id; // what entities does this entity reference? @ManyToMany private Set<Recursion> referencesTo; // what entities is this entity referenced from? @ManyToMany(mappedBy="referencesTo") private Set<Recursion> referencesFrom; public Recursion init() { referencesTo = new HashSet<>(); return this; } // getters, setters }

Y para usarlo debe tener en cuenta que la propiedad propietaria es las referencesTo . Solo necesita poner relaciones en esa propiedad para poder hacer referencia a ellas. Cuando lee una Entity hacia atrás, suponiendo que hace una fetch join , JPA creará las colecciones para el resultado. Cuando elimina una entidad, JPA eliminará todas las referencias a ella.

tx.begin(); Recursion r0 = new Recursion().init(); Recursion r1 = new Recursion().init(); Recursion r2 = new Recursion().init(); r0.getReferencesTo().add(r1); r1.getReferencesTo().add(r2); em.persist(r0); em.persist(r1); em.persist(r2); tx.commit(); // required so that existing entities with null referencesFrom will be removed from cache. em.clear(); for ( int i=1; i <= 3; ++i ) { Recursion r = em.createQuery("select distinct r from Recursion r left join fetch r.referencesTo left join fetch r.referencesFrom where id = :id", Recursion.class).setParameter("id", i).getSingleResult(); System.out.println(r + " To=" + Arrays.toString(r.getReferencesTo().toArray()) + " From=" + Arrays.toString(r.getReferencesFrom().toArray()) ); } tx.begin(); em.createQuery("delete from Recursion where id = 2").executeUpdate(); tx.commit(); // required so that existing entities with referencesTo will be removed from cache. em.clear(); Recursion r = em.createQuery("select distinct r from Recursion r left join fetch r.referencesTo left join fetch r.referencesFrom where id = :id", Recursion.class).setParameter("id", 1).getSingleResult(); System.out.println(r + " To=" + Arrays.toString(r.getReferencesTo().toArray()) + " From=" + Arrays.toString(r.getReferencesFrom().toArray()) );

Lo cual da la siguiente salida de registro (siempre verifica las declaraciones SQL generadas):

Hibernate: create table Recursion (id integer not null, primary key (id)) Hibernate: create table Recursion_Recursion (referencesFrom_id integer not null, referencesTo_id integer not null, primary key (referencesFrom_id, referencesTo_id)) Hibernate: create sequence hibernate_sequence start with 1 increment by 1 Hibernate: alter table Recursion_Recursion add constraint FKsi0wfuwfs0bl19jjpofw4n8pt foreign key (referencesTo_id) references Recursion Hibernate: alter table Recursion_Recursion add constraint FKarrkuyh2v1j5qnlui2vbpl7tk foreign key (referencesFrom_id) references Recursion Hibernate: call next value for hibernate_sequence Hibernate: call next value for hibernate_sequence Hibernate: call next value for hibernate_sequence Hibernate: insert into Recursion (id) values (?) Hibernate: insert into Recursion (id) values (?) Hibernate: insert into Recursion (id) values (?) Hibernate: insert into Recursion_Recursion (referencesFrom_id, referencesTo_id) values (?, ?) Hibernate: insert into Recursion_Recursion (referencesFrom_id, referencesTo_id) values (?, ?) Hibernate: select distinct recursion0_.id as id1_2_0_, recursion2_.id as id1_2_1_, recursion4_.id as id1_2_2_, references1_.referencesFrom_id as referenc1_3_0__, references1_.referencesTo_id as referenc2_3_0__, references3_.referencesTo_id as referenc2_3_1__, references3_.referencesFrom_id as referenc1_3_1__ from Recursion recursion0_ left outer join Recursion_Recursion references1_ on recursion0_.id=references1_.referencesFrom_id left outer join Recursion recursion2_ on references1_.referencesTo_id=recursion2_.id left outer join Recursion_Recursion references3_ on recursion0_.id=references3_.referencesTo_id left outer join Recursion recursion4_ on references3_.referencesFrom_id=recursion4_.id where id=? model.Recursion@7bdf6bb7 To=[model.Recursion@1bc53649] From=[] Hibernate: select distinct recursion0_.id as id1_2_0_, recursion2_.id as id1_2_1_, recursion4_.id as id1_2_2_, references1_.referencesFrom_id as referenc1_3_0__, references1_.referencesTo_id as referenc2_3_0__, references3_.referencesTo_id as referenc2_3_1__, references3_.referencesFrom_id as referenc1_3_1__ from Recursion recursion0_ left outer join Recursion_Recursion references1_ on recursion0_.id=references1_.referencesFrom_id left outer join Recursion recursion2_ on references1_.referencesTo_id=recursion2_.id left outer join Recursion_Recursion references3_ on recursion0_.id=references3_.referencesTo_id left outer join Recursion recursion4_ on references3_.referencesFrom_id=recursion4_.id where id=? model.Recursion@1bc53649 To=[model.Recursion@42deb43a] From=[model.Recursion@7bdf6bb7] Hibernate: select distinct recursion0_.id as id1_2_0_, recursion2_.id as id1_2_1_, recursion4_.id as id1_2_2_, references1_.referencesFrom_id as referenc1_3_0__, references1_.referencesTo_id as referenc2_3_0__, references3_.referencesTo_id as referenc2_3_1__, references3_.referencesFrom_id as referenc1_3_1__ from Recursion recursion0_ left outer join Recursion_Recursion references1_ on recursion0_.id=references1_.referencesFrom_id left outer join Recursion recursion2_ on references1_.referencesTo_id=recursion2_.id left outer join Recursion_Recursion references3_ on recursion0_.id=references3_.referencesTo_id left outer join Recursion recursion4_ on references3_.referencesFrom_id=recursion4_.id where id=? model.Recursion@42deb43a To=[] From=[model.Recursion@1bc53649] Hibernate: delete from Recursion_Recursion where (referencesTo_id) in (select id from Recursion where id=2) Hibernate: delete from Recursion_Recursion where (referencesFrom_id) in (select id from Recursion where id=2) Hibernate: delete from Recursion where id=2 Hibernate: select distinct recursion0_.id as id1_2_0_, recursion2_.id as id1_2_1_, recursion4_.id as id1_2_2_, references1_.referencesFrom_id as referenc1_3_0__, references1_.referencesTo_id as referenc2_3_0__, references3_.referencesTo_id as referenc2_3_1__, references3_.referencesFrom_id as referenc1_3_1__ from Recursion recursion0_ left outer join Recursion_Recursion references1_ on recursion0_.id=references1_.referencesFrom_id left outer join Recursion recursion2_ on references1_.referencesTo_id=recursion2_.id left outer join Recursion_Recursion references3_ on recursion0_.id=references3_.referencesTo_id left outer join Recursion recursion4_ on references3_.referencesFrom_id=recursion4_.id where id=? model.Recursion@6b739528 To=[] From=[]