Hibernate rekursive Viele-zu-Viele-Assoziation mit derselben Entität

Lesezeit: 9 Minuten

Hibernate rekursive Viele zu Viele Assoziation mit derselben Entitat
Magsol

Noch eine Frage zum Ruhezustand… 😛

Mit dem Annotations-Framework von Hibernate habe ich eine User Entität. Jeder User kann eine Sammlung von Freunden haben: eine Sammlung von anderen UserS. Ich konnte jedoch nicht herausfinden, wie man eine Many-to-Many-Assoziation innerhalb der User Klasse bestehend aus einer Liste von Users (unter Verwendung einer Zwischentabelle von Benutzerfreunden).

Hier ist die User-Klasse und ihre Anmerkungen:

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

Die benutzerfreundliche Zuordnungstabelle hat nur zwei Spalten, die beide Fremdschlüssel für die uid Spalte der tbl_users Tabelle. Die beiden Spalten sind personId (die dem aktuellen Benutzer zugeordnet werden sollte) und friendId (die die ID des Freundes des aktuellen Benutzers angibt).

Das Problem ist, dass das “friends”-Feld immer null ist, obwohl ich die Friends-Tabelle so ausgefüllt habe, dass alle Benutzer im System mit allen anderen Benutzern befreundet sind. Ich habe sogar versucht, die Beziehung auf umzustellen @OneToMany, und es kommt immer noch null heraus (obwohl die Hibernate-Debug-Ausgabe a . zeigt SELECT * FROM tbl_friends WHERE personId = ? AND friendId = ? Abfrage, aber sonst nichts).

Irgendwelche Ideen, wie man diese Liste ausfüllt? Danke schön!

@ManyToMany für sich selbst ist ziemlich verwirrend, da sich die Art und Weise, wie Sie dies normalerweise modellieren würden, von der Art und Weise des “Ruhezustands” unterscheidet. Ihr Problem ist, dass Ihnen eine andere Sammlung fehlt.

Stellen Sie sich das so vor: Wenn Sie “Autor”https://stackoverflow.com/”book” als Viele-zu-Viele zuordnen, benötigen Sie die Sammlung “Autoren” für Buch und die Sammlung “Bücher” für Autor. In diesem Fall repräsentiert Ihre “Benutzer”-Entität beide Enden einer Beziehung; Sie benötigen also die Sammlungen “Meine Freunde” und “Freunde von”:

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

Sie können immer noch dieselbe Assoziationstabelle verwenden, beachten Sie jedoch, dass Join- / InverseJon-Spalten in Sammlungen vertauscht werden.

Die Sammlungen “friends” und “friendOf” können übereinstimmen oder nicht (je nachdem, ob Ihre “Freundschaft” immer auf Gegenseitigkeit beruht) und Sie müssen sie natürlich nicht auf diese Weise in Ihrer API bereitstellen, aber so wird eine Zuordnung vorgenommen es im Ruhezustand.

  • Ich hatte gehofft, Sie würden mir zum dritten Mal zu Hilfe kommen 🙂 Es hat perfekt funktioniert und Ihre Erklärung klärt mein Verständnis von ER deutlich. Vielen Dank, noch einmal! 🙂

    – Magsol

    1. November ’09 um 3:53

  • Darf ich Freunde und FreundOf kombinieren? Und wenn nicht, dann brauche ich Hilfe bei jsonmanagedreference und jsonbackreference in einer Entität in dieser Frage: stackoverflow.com/questions/24563066/…

    – dikkini

    5. Juli ’14 um 7:50

  • Ich verwende Hibernate 5 und eine Sammlung reicht aus – die Verwendung von zwei erstellt doppelte Zeilen in tbl_friends.

    – mdziob

    26. Mai ’17 um 8:09

  • Wie kann ich in “tbl_friends” eine eindeutige Einschränkung haben, indem ich sowohl “friendId” als auch “personId” verwende?

    – Tayfun Yaşar

    7. Apr. ’18 um 7:56

  • Ich denke, es lohnt sich hinzuzufügen, dass, wenn dieselbe Tabelle verwendet wird, eine von ihnen ein “mappedBy” haben sollte und nicht die @JoinTable. Freund von zum Beispiel: @ManyToMany(mappedBy = "friends"). Andernfalls wird der Winterschlaf versuchen, beides fortzusetzen und zu einem führen java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'x-y' for key 'PRIMARY'

    – Lucas Noetzold

    11. September ’19 um 21:41 Uhr


1641667570 110 Hibernate rekursive Viele zu Viele Assoziation mit derselben Entitat
K.Nikolaus

Die akzeptierte Antwort scheint mit dem zu kompliziert zu sein @JoinTable Anmerkungen. Eine etwas einfachere Implementierung benötigt nur a mappedBy. Verwenden von mappedBy zeigt den Besitz an Entity, oder Eigentum, das wahrscheinlich der sein sollte referencesTo da würde das als “Freunde” gelten. EIN ManyToMany Beziehung kann einen sehr komplizierten Graphen erzeugen. Verwenden von mappedBy macht den Code so:

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

Und um es zu verwenden, müssen Sie berücksichtigen, dass das Eigentum das ist referencesTo. Sie müssen nur Beziehungen in diese Eigenschaft einfügen, damit auf sie verwiesen wird. Wenn du ein liest Entity zurück, vorausgesetzt du tust a fetch join, erstellt JPA die Sammlungen für das Ergebnis. Wenn Sie eine Entität löschen, löscht JPA alle Verweise darauf.

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

Was die folgende Protokollausgabe liefert (überprüfen Sie immer die generierten SQL-Anweisungen):

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=[]

  • Diese Antwort sollte die akzeptierte, einfache 1-Parameter-Lösung sein. Es erzeugt genau das, was Sie in einem gerichteten Graphen erwarten würden. Eine eindeutige Beziehungstabelle, die Kanten von a nach b darstellt und doppelte Informationen in separaten Spalten vermeidet.

    – Simplicity’s_Strength

    21. Juli ’21 um 12:54

Eigentlich ist es sehr einfach und könnte erreicht werden, indem man sagt, du hast folgende Entität

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 dafür:

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

Und Testfall

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

}

.

187000cookie-checkHibernate rekursive Viele-zu-Viele-Assoziation mit derselben Entität

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy