31

Inną kwestią Hibernate ...: PHibernate many-to-many związek z tym samym podmiotem

Korzystanie Adnotacje ramy Hibernate, mam podmiot User. Każdy model User może mieć kolekcję znajomych: kolekcję innych modeli User. Jednak nie byłem w stanie wymyślić, jak utworzyć powiązanie wiele do wielu w klasie User s, składające się z listy User s (przy użyciu tabeli pośredniej dla użytkowników znajomych).

Oto klasa użytkownika i jego adnotacje: tabela mapowania

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

Użytkownik przyjaciel ma tylko dwie kolumny, z których oba są klucze obce do kolumny tabeli tbl_usersuid. Dwie kolumny to: personId (która powinna być odwzorowana na bieżącego użytkownika) i friendId (która określa identyfikator znajomego bieżącego użytkownika).

Problem polega na tym, że pole "znajomi" wciąż wychodzi null, mimo że wstępnie wypełniłem tabelę znajomych tak, że wszyscy użytkownicy w systemie są przyjaciółmi z innymi użytkownikami. Próbowałem nawet zmienić relację na @OneToMany, a ona nadal wychodzi null (chociaż wynik debugowania Hibernuj pokazuje zapytanie SELECT * FROM tbl_friends WHERE personId = ? AND friendId = ?, ale nic więcej).

Wszelkie pomysły na wypełnienie tej listy? Dziękuję Ci!

Odpowiedz

47

@ManyToMany dla siebie jest dość mylące, ponieważ sposób w jaki normalnie modelujesz to różni się od sposobu "Hibernacji". Twój problem polega na tym, że brakuje Ci innej kolekcji.

Pomyśl o tym w ten sposób - jeśli mapujesz "autor"/"książkę" jako wiele-do-wielu, potrzebujesz kolekcji "autorów" w kolekcji Książki i "książek" na Autorze. W tym przypadku twoja jednostka "User" reprezentuje oba końce relacji; więc trzeba „moich przyjaciół” i „Przyjaciel” kolekcji:

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

Można nadal używać tej samej tabeli stowarzyszenie, ale należy pamiętać, że Wejścia/kolumn inverseJon są zamienione na zbiorach.

Kolekcje "friends" i "friendOf" mogą, ale nie muszą pasować (w zależności od tego, czy twoja "przyjaźń" jest zawsze wzajemna) i nie musisz ich ujawniać w ten sposób w swoim API, ale to sposób na mapowanie go w Hibernate.

+0

ja ufałem ci "Przyszedł mi na ratunek po raz trzeci :) Udało się to doskonale, a twoje wyjaśnienie znacznie wyjaśnia moje zrozumienie ER. Dziękuję bardzo, jeszcze raz! :) – Magsol

+1

Czy mogę łączyć przyjaciół i przyjaciół? A jeśli nie, potrzebuję pomocy z jsonmanagedreference i jsonbackreference w jednym obiekcie w tym pytaniu: http://stackoverflow.com/questions/24563066/hibernate-user-and-friends-jsonreference – dikkini

+0

Używam Hibernate 5 i jednej kolekcji wystarcza - użycie dwóch tworzy zduplikowane wiersze w tbl_friends. – mdziob

0

Właściwie jego bardzo prosta i może być osiągnięty poprzez następujące powiedzieć już po podmiotowi

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 na sam:

<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 Test case

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

}