Wie vermeidet man Duplikate mit JPA-Kaskaden?

8

Ich habe eine Parent Entität mit einer Child Entität in einer ManyToOne Beziehung:

%Vor%

Das Kind hat ein eindeutiges Feld:

%Vor%

Wenn ich ein neues Kind brauche, frage ich zuerst das ChildDAO :

%Vor%

Das Problem ist, wenn ich oben zweimal mag (mit demselben Namen für Kind ) und nur das Eltern bei beibehalten soll Am Ende bekomme ich eine Constraint-Ausnahme. Was normal erscheint, da in der Datenbank zunächst kein Kind mit dem angegebenen Namen existiert.

Das Problem ist, ich bin nicht sicher, was der beste Weg wäre, um diese Situation zu vermeiden.

    
Cos64 27.11.2011, 19:50
quelle

4 Antworten

9

Sie erstellen zwei nicht beständige Instanzen von Child mit new Child() zweimal und fügen diese dann in zwei verschiedene Parents ein. Wenn Sie die Parent-Objekte beibehalten, wird jede der beiden neuen Child-Instanzen per Kaskade beibehalten und eingefügt, wobei jede eine andere @Id hat. Die einzigartige Einschränkung für den Namen bricht dann. Wenn Sie CascadeType.ALL ausführen, erhalten Sie jedes Mal, wenn Sie new Child() ausführen, ein separates persistentes Objekt.

Wenn Sie wirklich möchten, dass die beiden Child-Instanzen als ein einzelnes persistentes Objekt mit derselben ID behandelt werden, müssen Sie diese separat beibehalten, um sie dem Persistenzkontext / der Persistenz zuzuweisen. Nachfolgende Aufrufe von childDao.findByName würden dann die Einfügung löschen und das neue Kind, das Sie gerade erstellt haben, zurückgeben, so dass Sie new Child() nicht zweimal ausführen.

    
wrschneider 27.11.2011, 20:47
quelle
6

Sie erhalten dies, weil Sie versuchen, ein bereits existierendes Objekt (gleiche ID) zu erhalten. Ihre Kaskade ist wahrscheinlich nicht persistent, es ist MERGE / UPDATE / REMOVE / DETACH. Der beste Weg, um diese Situation zu vermeiden, ist, die Kaskade richtig einzurichten oder die Kaskaden manuell zu verwalten. Kaskadierung ist grundsätzlich der Übeltäter.

Sie wollen wahrscheinlich so etwas:

%Vor%

Die Kaskaden können extrem frustrierend sein, wenn Sie dem Framework erlauben, es zu verwalten, weil Sie gelegentlich Aktualisierungen verpassen, wenn es zwischenspeichert (besonders bei Collections in ManyToOne-Beziehungen). Wenn Sie das Parent nicht explizit speichern und dem Framework erlauben, mit den Kaskaden umzugehen. Generell erlaube ich eine Cascade.ALL von meinem Elternteil in einer Beziehung und einer Kaskade von allen durch DELETE / PERSIST von meinen Kindern. Ich bin ein Eclipselink-Benutzer, daher ist meine Erfahrung mit Hibernate / other begrenzt und ich kann nicht mit dem Caching sprechen. Denken Sie darüber nach - wenn Sie ein Kind retten, möchten Sie wirklich alle damit verbundenen Objekte speichern? Wenn Sie ein Kind hinzufügen, sollten Sie das Elternteil nicht benachrichtigen? *

In meinem Fall hätte ich einfach eine "saveParent" und "saveChild" -Methode in meinem Dao / Service, die sicherstellt, dass der Cache korrekt ist und die Datenbank korrekt ist, um Kopfschmerzen zu vermeiden. Manuelle Verwaltung bedeutet, dass Sie absolute Kontrolle haben und Sie sich nicht auf die Kaskaden verlassen müssen, um Ihre schmutzige Arbeit zu erledigen. In einer einzigen Richtung sind sie fantastisch, in der Minute, in der sie "stromaufwärts" kommen, wirst du einige unerwartete Verhaltensweisen haben.

    
Daniel B. Chapman 27.11.2011 20:15
quelle
3

Wenn Sie zwei Objekte erstellen und JPA diese automatisch beibehalten, erhalten Sie zwei Zeilen in der Datenbank. Sie haben also zwei Möglichkeiten: Erstellen Sie keine zwei Objekte oder lassen Sie JPA diese nicht automatisch bestehen.

Um zu vermeiden, dass zwei Objekte erstellt werden, müssten Sie Ihren Code so anordnen, dass zwei Eltern die gleiche Instanz erhalten, wenn sie versuchen, untergeordnete Objekte mit demselben Namen zu erstellen. Sie möchten wahrscheinlich eine WeakHashMap (möglicherweise auf die aktuelle Anfrage beschränkt) durch die Angabe von lokalen Variablen), die mit dem Namen eingegeben werden, wobei Eltern den Namen ihres neuen Kindes nachsehen können, um zu sehen, ob das Kind bereits existiert. Wenn nicht, können sie das Objekt erstellen und es in die Karte einfügen.

Um zu vermeiden, dass JPA die Objekte automatisch persistiert, löschen Sie die Kaskade und verwenden Sie persistent , um die Objekte direkt nach der Erstellung manuell dem Kontext hinzuzufügen.

Da ein Persistenzkontext im Grunde eine ausgetrickste WeakHashMap ist, die an eine Datenbank angehängt ist, sind diese Ansätze ziemlich ähnlich, wenn es darauf ankommt.

    
Tom Anderson 27.11.2011 23:33
quelle
1

Sie legen ein untergeordnetes Objekt fest. Wenn das übergeordnete Objekt beibehalten wird, speichern Sie ein Register in der Datenbank, das auf ein nicht vorhandenes untergeordnetes Element verweist.

Wenn Sie ein neues Objekt erstellen, muss es vom EntityManager auf die gleiche Weise verwaltet werden, wenn Sie ein Objekt mit dem DAO "finden", das Register aus DB holen und das Objekt in den EntityManager-Kontext setzen muss.

Versuchen Sie zunächst, das untergeordnete Objekt beizubehalten oder zusammenzuführen.

    
EricSonaron 27.11.2011 20:06
quelle

Tags und Links