Hibernuj rekurencyjne skojarzenie wielu do wielu z tą samą jednostką
Kolejne pytanie Hibernate... : P
Używając frameworka adnotacji Hibernate, mam encję User
. Każda User
może mieć zbiór znajomych: zbiór innych User
s. jednak nie byłem w stanie dowiedzieć się, jak stworzyć wiele do wielu skojarzeń w User
klasie składającej się z listy User
s (używając tabeli pośredniej user-friends).
Oto Klasa użytkownika i jej adnotacje:
@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;
}
Tabela mapowania znajomego użytkownika ma tylko dwa kolumny, z których oba są kluczami obcymi do kolumny uid
tabeli tbl_users
. Dwie kolumny to personId
(która powinna mapować do bieżącego użytkownika) i friendId
(która określa id znajomego bieżącego użytkownika).
Problem w tym, że pole "znajomi" ciągle wychodzi null, mimo że wstępnie wypełniłem tabelę znajomych tak, że wszyscy użytkownicy w systemie są przyjaciółmi wszystkich innych użytkowników. Próbowałem nawet zmienić relację na @OneToMany
i nadal wychodzi null (choć wyjście debugowania Hibernate pokazuje zapytanie SELECT * FROM tbl_friends WHERE personId = ? AND friendId = ?
, ale nic więcej).
Jakieś pomysły, jak wypełnić tę listę? Dziękuję!
3 answers
@ManyToMany to self jest raczej mylące, ponieważ sposób, w jaki normalnie modelujesz, różni się od sposobu" Hibernate". Twój problem polega na tym, że brakuje Ci kolejnej kolekcji.
Pomyśl o tym w ten sposób - jeśli mapujesz "autor" / "książka" jako wiele do wielu, potrzebujesz kolekcji "autorzy" na książce i" książki " na Autor. W tym przypadku Twoja jednostka "użytkownik" reprezentuje oba końce relacji; więc potrzebujesz kolekcji" moi przyjaciele "i" przyjaciel": {]}
@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;
Możesz nadal używać ta sama tabela asocjacji, ale zauważ, że kolumny join / inverseJon są zamieniane na kolekcjach.
Kolekcje "friends" I "friendOf" mogą lub nie pasować (w zależności od tego, czy twoja "przyjaźń" jest zawsze wzajemna) i oczywiście nie musisz wystawiać ich w ten sposób w swoim API, ale w ten sposób mapujesz je w Hibernate.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-01 02:57:34
W rzeczywistości jest to bardzo proste i może być osiągnięte przez następujące powiedzmy, że masz następujący podmiot
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 dla tego samego:
<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>
I przypadek testowy
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();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-01 11:31:47
Przyjęta odpowiedź wydaje się zbyt skomplikowana z @JoinTable
adnotacjami. Nieco prostsza implementacja wymaga tylko mappedBy
. Użycie mappedBy
oznacza posiadanie Entity
, czyli własność, która prawdopodobnie powinna być referencesTo
, ponieważ byłoby to uważane za"przyjaciół". Relacja ManyToMany
może stworzyć bardzo skomplikowany Wykres. Użycie mappedBy
sprawia, że kod jest tak:
@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
}
I aby z niego korzystać należy wziąć pod uwagę, że właścicielem nieruchomości jest referencesTo
. Wystarczy tylko umieścić relacje w tym własności w celu ich odwołania. Po przeczytaniu Entity
z powrotem, zakładając, że wykonasz fetch join
, JPA utworzy kolekcje dla wyniku. Gdy usuniesz encję, JPA usunie wszystkie odniesienia do niej.
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()) );
Który daje następujące wyjście dziennika (zawsze sprawdzaj wygenerowane instrukcje SQL):
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=[]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-13 21:26:13