JPA2/Hibernate: Many-to-Many-Relation wird u.a. beim löschen nicht aktualisiert

T

tuxedo

Gast
Hallo zusammen,

ich hab ein kleines Hibernate/JPA Verständnis-Problem bei dem ich nicht weiter komme.

Ich hab folgende Entitäten:

Code:
EmUser:
-id
-name
-Set<EmUserGroup> groups <---------- MANY-TO-MANY

Code:
EmUserGroup:
-id
-name
-Set<EmUser> users <---------- MANY-TO-MANY - OWNER
-Set<EmPermission> permissions <---------- MANY-TO-MANY - OWNER

Code:
EmPermission:
-id
-name
-Set<EmUserGroup> groups <---------- MANY-TO-MANY


Alle Many-to-Many Beziehungen sind auf Eager-Loading eingestellt. Die Entität EmUserGroup ist der Owner der Many-to-Many Beziehungen. Die Cascade-Optionen habe ich nach bestem WIssen und Gewissen konfiguriert und schon verschiedene Konstellationen ausprobiert.

Folgendes Szenatio möchte ich unter anderem gerne realisieren:

Ich habe verschiedene User die in verschiedenen Gruppen sind. Wenn ich nun eine Gruppe lösche, dann sollen die User bestehen bleiben, aber die Gruppenzugehörigkeit zur gelöschten Gruppe soll verloren gehen.
Im Detail:

Mein EntityManager hat diverse User-Entitäten bereits geladen. Irgendwann später soll eine Gruppe gelöscht werden. Das löschen funktioniert.
Aber irgendwie schafft es der EntityManager nicht, die bereits geladenen User dahingehen zu aktualisieren, dass eine bestimmte Gruppe nun nicht mehr da ist und demnach die User-Group-Relation aktualisiert werden muss. Nach dem löschen ist in der Liste mit Gruppen der betroffenen User die gelöschte Gruppe noch vorhanden. Der EntityManager der die User geladen hat und die Gruppe gelöscht hat ist ein und derselbe. Warum geht das nicht?

Ich hab das ganze (Maven-)Projekt mal gezippt und angehängt. Ist einfach zu groß um die ganzen Klassen hier zu posten. Das ganze ist auf das notwendigste reduztiert und ist prinzipiell lauffähig. Wäre echt klasse wenn mir da jemand weiterhelfen kann.

Mein Gefühl sagt mir dass ich entweder

a) die Sache mit dem Cascade falsch mache

oder

b) das was ich vor habe von JPA2 gar nicht komplett abgedeckt wird und ich teilweise selbst Hand anlegen muss




Gruß
Alex

Ach ja: Ich hab keine Annotationen benutzt, da ich in der späteren Anwendung detachte Entitäten serialisieren/deserialisieren will und dort keine JPA-Abhängigkeit durch verwendung von Annotationen vorhanden sein soll. Auch handelt es sich NICHt um eine JEE Anwendung. Verwende Hibernate/JPA hier im Desktop-Umfeld.
 

Anhänge

  • hibernatetests.zip
    8,5 KB · Aufrufe: 9
T

tuxedo

Gast
Um die Frage noch etwas zu konkretisieren.

Das hier funktioniert nicht: Der User hat hinterher immer noch die Gruppe 2:

Java:
            EmUser foundUser = em.find(EmUser.class, 1l);
            
            System.out.println("found usergroup: "+foundUser.getGroups().toString());
            System.out.println("Removing Group id=2");
            em.getTransaction().begin();
            EmUserGroup groupToRemove = em.find(EmUserGroup.class, 2l);
            em.remove(groupToRemove);
            em.getTransaction().commit();
            System.out.println("after remove:    "+foundUser.getGroups().toString());

Wenn ich selbst hand anlege und die Relation aufbreche, dann geht's:

Java:
           EmUser foundUser = em.find(EmUser.class, 1l);
            
            System.out.println("found usergroup: "+foundUser.getGroups().toString());
            System.out.println("Removing Group id=2");
            em.getTransaction().begin();
            EmUserGroup groupToRemove = em.find(EmUserGroup.class, 2l);
            Set<EmUser> users = groupToRemove.getUsers();
            for (EmUser emUser : users) {
                emUser.getGroups().remove(groupToRemove);
                em.persist(emUser);
            }
            Set<EmPermission> permissions = groupToRemove.getPermissions();
            for (EmPermission emPermission : permissions) {
                emPermission.getGroups().remove(groupToRemove);
                em.persist(emPermission);
            }
            em.remove(groupToRemove);
            em.getTransaction().commit();
            System.out.println("after remove:    "+foundUser.getGroups().toString());

Die Frage ist: Muss ich wirklich selbst aktiv werden und die Relationen aufheben? Oder kann das Hibernate nicht für mich tun? Wenn ja: Wie?!
 
M

maki

Gast
Hi,

mal ganz unabhängig von JPA und Cascaden, rein zumThema OO:
Es wäre imho keine gute Sache, dass man einfach so eine Gruppe löscht, und diese dann in allen Entitäten automatisch verschwindet, Kapselung und so.

Denke das der EM die Groupe wieder einfügt, weil er merkt dass diese noch referenziert wird, nachdem du versuchst sie explizit zu löschen, ohne die Gruppe aus EmUser und EmUserGroup zu entfernen.

Nach einem nur flüchtigen Blick in die Sourcen, fehlen mir die Möglichkeiten der "sauberen" Manipulation von EmUser und EmUserGroup, die reichen alle nur ihre Collections nach aussen.
addUser in der EmUserGroup und addUserGroup in EmUser wären imho Pflicht, natürlich mit den entsprechenden remove Methoden.
 
T

tuxedo

Gast
Zum Thema Kapselung: Ja, das ist mir bewusst. Da wird jedoch an anderer Stelle im eigentlichen Programm sorge getragen. Keine Angst ;-)

Denke das der EM die Groupe wieder einfügt, weil er merkt dass diese noch referenziert wird, nachdem du versuchst sie explizit zu löschen, ohne die Gruppe aus EmUser und EmUserGroup zu entfernen.

Naja, so wie ich das gesehen habe wird die Gruppe gelöscht, aber die Join-Table beinhaltet nach wie vor eine Referenz. Nur ist die halt "kaputt", da zur einen Seite keine Entity mehr da ist.

Nach einem nur flüchtigen Blick in die Sourcen, fehlen mir die Möglichkeiten der "sauberen" Manipulation von EmUser und EmUserGroup, die reichen alle nur ihre Collections nach aussen.
addUser in der EmUserGroup und addUserGroup in EmUser wären imho Pflicht, natürlich mit den entsprechenden remove Methoden.

Das ist wie gesagt nur ein Minimal-Beispiel. In der tatsächlichen Anwendung gibt es das alles.

Jetzt steh ich aber nach wie vor vor einer unbeantworteten Frage wie sich das mit dem fortpflanzen einer Änderung, die sich normalerweise auf eine Relation auswirkt, verhält...

Hab im Netz dazu diverses gelesen. Und als eine Lösung taucht da immer wieder auf, dass man sich selbst um das aufbrechen der Beziehung kümmern muss. Aber irgendwie ist das unschön. Zumal mir dann der Nutzen von JPA irgendwie nicht klar wird wenn ich mich auch noch um solche Dinge kümmern muss.

- Alex
 
M

maki

Gast
Naja, so wie ich das gesehen habe wird die Gruppe gelöscht, aber die Join-Table beinhaltet nach wie vor eine Referenz. Nur ist die halt "kaputt", da zur einen Seite keine Entity mehr da ist.
Das dürfte gar nicht sein, wenn die DB eine RDBMS ist (MySQl mit MyISAM Tabellen ist zB. kein RDBMS ;)) und auf die contrainsts bez. des Foreign Keys achtet.

Aber wie gesagt, imho sollte der EM das lsöchen nicht wirklich durchführen.
 

JanHH

Top Contributor
geht das überhaupt, manyToMany mit eager loading? hab mal gelesen dass das nicht geht. kommt mir auch nicht sinnvoll vor (Zitat eines Freundes.. "da lädt man schnell mal die halbe Datenbak").
 
T

tuxedo

Gast
@maki

Ja, hab mich tatsächlich vertan. In der DB wird in der Join-Tabelle aufgeräumt, aber die bereits geladenen Entitäten von ein und demselben EM bekommen das nicht mit :-(

@JanHH

geht das überhaupt, manyToMany mit eager loading? hab mal gelesen dass das nicht geht.

Also wenn ich mich im Buch "Pro JPA2: Mastering the Java Persistence API" nicht verlesen habe, dann geht das sehr wohl.


kommt mir auch nicht sinnvoll vor (Zitat eines Freundes.. "da lädt man schnell mal die halbe Datenbak").

Dann sag deinem Freund 'nen schönen Gruß: Es gibt seht wohl Anwendungsfälle wo das Sinn macht und die Datenbank auch nicht so groß ist dass das zu einem Problem werden würde. Klar, im Allgemeinen ist davon abzuraten. Aber im Speziellen kann das durchaus Sinn machen. Denn wenn es zu 100.0% auszuschließen wäre dass das auch nur im entferntesten Sinn macht: Gäbe es diese Funktion dann? Wohl kaum. Aber in diesem Fall: Die Funktion gibt es, also kann und darf man sie, wenn man alle Warnungen dazu gelesen und verstanden hat, auch benutzen.
Aber muss ich mich jetzt rechtfertigen?

Bis dato hab ich nur zu lesen bekommen dass es es nicht tun sollte, das keine gute Idee wäre etc. Okay, Verstanden und zur Kenntnis genommen. Dennoch:

Keiner geht aber drauf ein ob ich hier von JPA tatsächlich eine Funktion erwarte die es nicht gibt... ???:L

- Alex
 
M

maki

Gast
Hast du es mal mit cascade-remove probiert?

In der DB wird in der Join-Tabelle aufgeräumt, aber die bereits geladenen Entitäten von ein und demselben EM bekommen das nicht mit
Denke schon dass er es mitbekommt und aktiv verhindert.
 
T

tuxedo

Gast
Ja, hab ich. Dann entfernt er aber auch alle User wenn ich ne Gruppe lösche ... Nicht so der gewünschte Effekt.
 
G

Gelöschtes Mitglied 5909

Gast
Auch wenn das jetzt nicht zu der Lösung Beiträgt:

Wieso verzichtest du nicht auf den Cascade Mechanismus und machst es selber?

Schreib dir doch n Service der das (und andere dinge du die brauchst) macht.
 
T

tuxedo

Gast
Weiß denn hier keiner soweit Bescheid dass er klipp und klar sagen kann: Nein JPA kann das nicht es führt kein Weg am selber machen vorbei...Oder: Ja, JPA kann das. Such doch mal nach "xyz", dann weißt du mehr.

Dass ich's prinzipiell selber machen kann weiß ich selbst. Aber wieso selbst schreiben wenn's das schon gibt. [ironie]Ich mein, ich könnte ja auch JPA/Hibernate über Bord werfen und wieder das gute alte JDBC nutzen und alles selbst machen. Dann weiß ich wenigstens wie's und ob's funktioniert.[/ironie]
 
Zuletzt bearbeitet von einem Moderator:
G

Gelöschtes Mitglied 5909

Gast
@maki

Ja, hab mich tatsächlich vertan. In der DB wird in der Join-Tabelle aufgeräumt, aber die bereits geladenen Entitäten von ein und demselben EM bekommen das nicht mit

Passen die Transaktionen?

Hast du mal ein refresh() probiert?
 
M

maki

Gast
Weiß denn hier keiner soweit Bescheid dass er klipp und klar sagen kann: Nein JPA kann das nicht es führt kein Weg am selber machen vorbei...Oder: Ja, JPA kann das. Such doch mal nach "xyz", dann weißt du mehr.
Offensichtlich nicht, ist ja auch etwas, dass man normalerweise niemals machen würde ;)

Dass ich's prinzipiell selber machen kann weiß ich selbst. Aber wieso selbst schreiben wenn's das schon gibt.
JPA bietet nicht für jeden obskuren Anwendungsfall eine implizite Lösung.

@raiL
Ohne entsprechende Cascade wird das nix, der EM kann ja schlecht Entitäten entfernen, die noch referenziert werden.
 
T

tuxedo

Gast
@maki

Offensichtlich nicht, ist ja auch etwas, dass man normalerweise niemals machen würde

Ja, das mag sein.

Ich hab nochmal gegoogelt und meine Bücher gewälzt. Über google lande ich immer wieder auf stackoverflow.com. Dort wird geschrieben, dass man entweder die ManyToMany Relation selbst aufheben, und dann die Entität löschen soll, oder aber als alternative die JoinTable doppelt bestrücken. Also nicth nur A auf B zeigen lassen, sondern auch B auf A. Dann hat man zwar für jede beziehung zwei Einträge in der Tabelle, aber JPA kriegt das mit dem auflösen der Beziehung von alleine hin. Allerdings scheint das mehr ein Hack als eine tolle Lösung zu sein.

Nachdem ich dann nochmal meine Bücher konsultiert habe, bin auch das hier gestoßen:

Pro JPA2: Mastering the Java Persitence API hat gesagt.:
Relationship maintenance is the responsibility of the application. We will repeat this statement
over the course of this book, but it cannot be emphasized enough. Almost every problem related to
removing an entity always comes back to this issue. If the entity to be removed is the target of foreign
keys in other tables, those foreign keys must be cleared for the remove to succeed. The remove
operation will either fail as it did here or it will result in stale data being left in the foreign key
columns referring to the removed entity in the event that there is no referential integrity.

In dem Umfeld habe ich im gleichen Buch auch zwischen den Zeilen lesen können, dass die Cascade-Geschichte wohl nicht der Standardfall ist. Überlicherweise managed man die Beziehungen selbst und hebt sie auch selbst wieder auf. Cascade benutzt man nur da, wo man sich sicher ist, dass es keine Kettenreaktion auf die halbe DB auslöst und man sich durch diese Annotation/Konfiguration ein wenig "arbeit" ersparen kann. Vor allem wenn an mehreren Stellen mit den Entitäten "gearbeitet" wird.

Mein persönliches Fazit lautet also:

Ich lass den Cascade-Krempel weitgehend weg und mach das in meiner UserManagerment Klasse selbst.

- Alex
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
W JPA JPA2 - Eigene Klassen/Typen als Persistenzattribute, Best Practice Data Tier 2
D von Datenbank zu JPA2.0 code - geht das? Data Tier 3
T Hibernate/Spring JPA: eigene ID generieren Data Tier 5
Avalon @ManyToOne Hibernate oder JPA? Data Tier 5
D Hibernate Hibernate mit MariaDB Data Tier 1
ToBJo Hibernate Glassfish deploy mit Hibernate schlägt fehl Data Tier 1
C JPA Hibernate Map<String,String> richtig mappen Data Tier 2
S JPA Hibernate Search & EclipseLink (oder OpenJPA) Data Tier 0
R JPA Probleme mit Wechsel von EclipseLink auf Hibernate Data Tier 4
ARadauer Hibernate Entität readonly laden... Data Tier 1
G Hibernate SQL in Hibernate: Keine Parameter mit Index? Data Tier 2
P Wildfly + Hibernate + SQL Server Data Tier 0
E JPA Hibernate Query mit Timestamp hat seltsames Verhalten Data Tier 1
M Eclipse 4 RCP Hibernate Problem Data Tier 3
C Hibernate ProgressBar updaten mit Daten aus Hibernate Data Tier 4
B Hibernate und MySQL testen Data Tier 8
I Hibernate HQL: generiertes SQL ausgeben Data Tier 1
R mapping-file für hibernate zum Überschreiben der Annotationen Data Tier 7
R Hibernate Hibernate und Logback Data Tier 2
R Hibernate möchte Schema zwei mal undeployen Data Tier 2
F Hibernate Hibernate / JPA Data Tier 4
E Hibernate: Session vs EntityManager Data Tier 3
C Hibernate Hibernate Code Generation Data Tier 3
S Hibernate Mehrfachverbindung mit Hibernate Data Tier 3
M Hibernate Einstiegsfrage Data Tier 5
M Exception in thread "main" org.hibernate.MappingException: java.lang.ClassNotFoundException: Message Data Tier 4
S Hibernate Einstieg in Hibernate 3.2 sinnvoll? Data Tier 8
P JPA Eigene Vererbungsstrategie mit JPA / Hibernate Data Tier 2
J Hibernate Problem bei Master-Detail-Tabellen Data Tier 5
Y Jboss seam-hibernate-jpa Data Tier 5
RaoulDuke Hibernate Map<String,String> mit Annotations mappen Data Tier 2
M Hibernate Hibernate with GWT Data Tier 4
C Hibernate JPA mysql db erstellen Data Tier 4
M Hibernate Hibernate liest Daten zu oft aus! Data Tier 16
pg1337 Hibernate Fragen Data Tier 11
D Probleme bei Left Joins mit Hibernate createCriterias() Data Tier 2
D Hibernate probleme mit Verlinkungstabelle Data Tier 4
2 Hibernate Annotations Data Tier 7
G Hibernate select update no wait Data Tier 8
Z Hibernate: Many-To-Many nur eine bestimmte Spalte Data Tier 3
K Hibernate - Envers - Erzeugung der SQL Skripte Data Tier 4
G Hibernate 1:n Beziehung mit Vererbung Data Tier 5
D Hibernate-Criteria-API (Projections und MAX-Funktion) Data Tier 6
L Hibernate: failed to lazily initialize a collection of role Data Tier 3
S Hibernate hibernate.cfg.xml Data Tier 14
D JPA vs Hibernate.cfg und Entitymanager Data Tier 6
H Hibernate - Mapping für Enumeration Data Tier 1
R Hibernate Criteria Abfrageproblem Data Tier 2
A Hibernate und jdbc zusammen Data Tier 4
D Mit Hibernate aus JUnit ein DB-Schema erzeugen Data Tier 6
S [Hibernate] No Persistence provider for EntityManager Data Tier 5
B Problem mit org.hibernate.LazyInitializationException Data Tier 11
G Hibernate HQL und Interface Data Tier 4
G JSF Hibernate no session or session was closed Data Tier 12
S (Hibernate) Mapping einer Datenbanktabelle mit mehreren Fremdschlüssel Data Tier 7
X [Hibernate] Zusammengesetzte Entities möglich? Data Tier 7
N Hibernate Fake? Data Tier 2
S Problem beim Insert mit Hibernate Data Tier 9
V Hibernate Projection Data Tier 2
T org.hibernate.impl.SessionFactoryImpl Memory Leak Data Tier 10
G Hibernate Composite key Data Tier 11
X [Hibernate] Connection Pool - MinSize ? Data Tier 2
R Hibernate Criteria OR Data Tier 2
T hibernate/jpa abgefragte Listen immer mit Null-Werten gefüllt Data Tier 8
X [Hibernate] Anderen Connection Pool - Vorschläge? Data Tier 3
ARadauer Hibernate DDL Loggen Data Tier 6
G Hibernate abfrage Collection Data Tier 3
X [Hibernate] ReverseEngineering - Eigene Strategy verwenden? Data Tier 3
R Hibernate Criteria .group größer als Data Tier 5
R Hibernate daten laden Data Tier 7
H [Hibernate]1:1 Beziehung Data Tier 8
H [Hibernate]No CurrentSessionContext configured! Data Tier 6
X [Hibernate] Lässt sich die Dauer eines SELECTs loggen? Data Tier 4
R Hibernate n:n Relationtabelle mit Date Data Tier 3
H [Hibernate] Unknown Entity Data Tier 3
H [Hibernate] Configuration Data Tier 3
C [Hibernate] Generierung von hbm.xml to Java Data Tier 4
lumo Eclipse & JPA & Hibernate & Derby Data Tier 5
J Zufallsauswahl aus ResultList bei JPA(Hibernate) / Performance Data Tier 3
M Hibernate: Datum 0001-01-01 erzeugt null-Datum Data Tier 4
G Datenbankzugriff mit Hibernate Data Tier 7
Y Hibernate - Angabe des Schemas Data Tier 6
LadyMilka (Hibernate) in Criteria implizierter Join durch Subquery's Data Tier 8
M Hibernate Mehr als 1 Object speichern? Data Tier 18
M Unerklärliche Hibernate Exception Data Tier 20
LadyMilka (Hibernate) subquery in FROM-Clause Data Tier 9
haemi Viele DTOs in hibernate IdentityMap Data Tier 3
LadyMilka (hibernate) UNION dem Dialekt hinzufügen Data Tier 3
M Hibernate + Oracle 10g XE Data Tier 3
lumo Hibernate - entity class not found Data Tier 5
P SQL PRoblem Hibernate? Data Tier 8
J Vererbung mit JPA / Hibernate - pro/contra Data Tier 3
T JBoss/Hibernate: Abfrage dauert lang + hohe CPU? Data Tier 19
7 Hibernate-Abfrage (SubSelect im FROM) Data Tier 2
G Hibernate: many-to-one - Verwaiste Datensätze löschen Data Tier 2
G Layer für Datenbankzugriff Hibernate Data Tier 5
G Hibernate Zwischentabelle Data Tier 2
Java.getSkill() Hibernate und Spalte vom Typ xml Data Tier 6
G Hibernate 0...1 : 1 Beziehung Data Tier 6
G Hibernate mehrere @oneToone Data Tier 2

Ähnliche Java Themen

Neue Themen


Oben