-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 
Author Message
 Post subject: Speichern von indirekt referenzierten Objekten
PostPosted: Thu Aug 07, 2008 11:31 am 
Newbie

Joined: Tue Jul 08, 2008 10:56 am
Posts: 8
Hallo *,

ich habe ein Objekt von der Klasse A, das enthält eine Map von Objekten der Klasse B. Jetzt gibts zusätzlich noch eine Klasse BTupel, die von B abgeleitet ist, und 2 Objekte vom Typ B referenziert, der komplette Objektbaum sieht also so aus:


Code:
    B1
  /
A - B2
  \
   BTupel1 - B3
           \
             B4


Nun möchte ich A und alle abhängigen Bs persistieren. BTupel ist nur eine Hilfsklasse, die zur Laufzeit verwendet wird, und deren Instanzen nicht selbst persistiert werden sollen, sondern die referenzierten Bs. Es müssen also nur A und die tatsächlichen Bs persistiert werden, im obigen Beispiel A, B1, B2, B3 und B4. Wenn ich A und B einfach annotiere, werden A, B1, B2 und BTupel1 persistiert.

Die manuelle Variante wäre, neben der B-Map noch eine Liste zu pflegen, die Bs enthält, aber keine BTupel. Das ist unbefriedigend, da ich 2 Collections synchron halten muss.

Ein Versuch war, einen "UserCollectionType" für die B-Map schreiben, dessen getElementsIterator-Methode einen Iterator über alle Bs zurückliefert, aber das funktioniert nicht, denn anschließend kann Hibernate meine Bs nicht mehr persistieren: beim Initialisieren über Spring gibts eine org.hibernate.AnnotationException: Associated class not found: dings.test.B. Einen UserType für die Bs zu schreiben, denke ich bringt auch nix, denn ich muss ja bei manchen Bs mehrere Einträge generieren.

Eigentlich müsste ich doch nur beim Speichern eines Bs eingreifen: wenn es ein echtes B ist, wirds persistiert, wenn es ein BTupel ist, werden statt dessen die abhängigen Bs persistiert (also 2 Objekte statt einem). Wo kann ich mich da beim Persistieren einklinken? Oder funktioniert das ganz anders?

Dank im Voraus für irgendwelche Tipps,
Martin.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 08, 2008 2:44 am 
Expert
Expert

Joined: Thu Jul 05, 2007 9:38 am
Posts: 287
Hi,
ich fürchte ein derartiges vorgehen wird ziemlich schwierig zu implementieren sein, da es gegen die Idee von Hibernate geht: Man konstruiere einen Objektgraphen in Java und speichert ihn. Fertig.

Ich würde eher versuchen das technische Problem zu umgehen, in dem man das fachliche Problem anders löst. Das fachliche Problem habe ich aber noch gar nicht verstanden.

Konkret frage ich mich:

Wenn die BTupel nicht persistiert werden sollen, warum gibt es sie dann mitten zwischen persistenten Objekten?

Wie soll das konstrukt denn aussen, wenn es gespeichert ist? Sollen die Bs aus dem Tupel einfach ohne Referenz in der DB liegen? Oder sollen sie Inhalt der Map sein? Wenn ja unter welchem Schlüssel.

Ohne konkretere Angaben zu diesen Fragen kann ich nur den allgemeinen Rat geben: bring die Objektstruktur in die Form, die sie haben nach dem neu laden wieder haben soll, dann sollte sie sich auch relativ problemlos speichern lassen.

_________________
Please rate useful posts.


Schauderhaft: Softwaredevelopment, Projectmanagement, Qualitymanagement and all things "schauderhaft"


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 08, 2008 3:29 am 
Newbie

Joined: Tue Jul 08, 2008 10:56 am
Posts: 8
Danke für die Antwort.

Das fachliche Problem ist folgendes: einige der Bs sind historisiert, d.h. sie haben eine gueltigVon- und eine gueltigBis-Spalte, und wenn sich ein Wert ändert, wird ein neues B erzeugt und vom alten B gueltigBis aufs aktuelle Datum gesetzt. Das BTupel kapselt das aktuelle und das neue B und sorgt dafür, dass diese entsprechend aktualisiert werden. Damit brauch ich in der GUI beim Anzeigen von A nur Bs behandeln, und beim Editieren kann ich mit BTupeln arbeiten, die mir die Verwaltung historisierten Bs übernehmen.

Beim Laden aus der DB brauche ich nur die aktuellen Bs lesen, diese werden unter ihrem "Namen" in der Map gespeichert. Evtl. vom Benutzer neu erzeugte Bs kann ich nicht auch in die Map legen, da der Name gleich bleibt, nur die gueltig*-Spalten unterscheiden sich. Für die historisierten Bs erzeuge ich im Nachgang ein BTupel, das ich in die Map stecke.

Beim Speichern hingegen gibt es u.U. noch zusätzliche neue Bs, die gespeichert werden müssen. Daher war meine Idee, das Problem zu lösen, indem ich Hibernate sage: wenn Du auf ein BTupel triffst, rufe meinen Code auf, in dem ich Dir dann 2 Bs gebe, die Du dann speicherst. Das ist zwar unüblich, aber ich hätte erwartet, dass es trotzdem geht.

Alternativ bleibt mir nur die Möglichkeit, neben der Map mit den BTupeln noch eine andere Struktur zu verwalten, in der die Bs direkt drinliegen. Das wollte ich halt vermeiden.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 08, 2008 7:37 am 
Expert
Expert

Joined: Thu Jul 05, 2007 9:38 am
Posts: 287
Ich würde empfehlen, Bs einfach sauber zu modelieren. Eine Modellierungsmöglichkeiten wären:

Ein B hat eine Liste von früheren Bs
Ein B hat einen Vorgänger vom Typ B

Beides würde dazu führen, dass dein Hibernate Probleme sich quasi von alleine lösen und das 'komische' Tupel fällt auch weg.

Jens

_________________
Please rate useful posts.


Schauderhaft: Softwaredevelopment, Projectmanagement, Qualitymanagement and all things "schauderhaft"


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 08, 2008 9:35 am 
Newbie

Joined: Tue Jul 08, 2008 10:56 am
Posts: 8
Nochmal hallo,

das Tupel-Objekt haben wir eingeführt, weil es im Endeffekt 1:1 der GUI entspricht. Außerdem müssen wir nur die aktuellen Werte laden und nie übers Objektmodell auf die Vorgängerwerte zugreifen.

Dein Vorschlag würde die Navigation in der Historie ermöglichen, macht das Laden aller historisierten Werte möglich aber nicht nötig, lässt sich genau so einfach auf der GUI abbilden und entspricht sowohl der fachlichen Sicht wie auch der DB-Abbildung. Es scheint also mal wieder ein mächtigeres aber trotzdem einfacheres Konzept zu sein.

Vielen Dank fürs Auflösen der Betriebsblindheit!

Was aber bleibt ist das Problem beim Speichern: nach dem Laden müsste ich für die historisierten Bs ein neues B erzeugen, das die Referenz auf das Vorgängerobjekt bekommt und dieses in der Map von A ersetzt. Dadurch kann die GUI das entsprechende Feld anzeigen. Beim Speichern sollen aber die neuen Bs nur persistiert werden, wenn der Benutzer dort auch was eingegeben hat, ansonsten nicht. Wie bring ich Hibernate das bei?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 08, 2008 11:58 am 
Expert
Expert

Joined: Thu Jul 05, 2007 9:38 am
Posts: 287
Ich würde denken, wenn das aktuelle B immer das gleich bleibt, bloß der Vorgänger immer bei Bedarf neu als Kopie des aktuellen Bs erzeugt wird, bevor dies tatsächlich geändert wird, müsste es funktionieren.

Die Map enthält immer das aktuelle B und falls ein 'neuer Vorgänger' erzeugt wurde, wird der per cascade Einstellung mit gespeichert.

_________________
Please rate useful posts.


Schauderhaft: Softwaredevelopment, Projectmanagement, Qualitymanagement and all things "schauderhaft"


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 08, 2008 12:19 pm 
Newbie

Joined: Tue Jul 08, 2008 10:56 am
Posts: 8
Neinnein, es ist ja umgekehrt. Es wird kein neuer Vorgänger erzeugt, sondern ein neuer Nachfolger. A ist folgendermaßen in der DB gespeichert:
Code:
    B1
  /
A - B2
  \
    B3aktuell

Nach dem Laden mach ich das daraus:
Code:
    B1
  /
A - B2
  \
    B3neu - B3aktuell


B3aktuell ist jetzt der Vorgänger von B3neu ist, denn wenn ein neues B erstellt wird, ersetzt es natürlich das aktuelle. Auf der GUI brauch ich das Objekt ja immer, weil es z.B. als Textfeld vorhanden ist, ob es dann aber mit persistiert wird, ergibts erst aus dem Inhalt, der nur dann gesetzt ist, wenn der Benutzer was einträgt. Nur in diesem Fall soll das neue B mit abgespeichert werden. Wenn der Inhalt leer ist, soll nix gemacht werden.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Aug 08, 2008 4:44 pm 
Expert
Expert

Joined: Thu Jul 05, 2007 9:38 am
Posts: 287
tchick wrote:
Beim Speichern sollen aber die neuen Bs nur persistiert werden, wenn der Benutzer dort auch was eingegeben hat, ansonsten nicht. Wie bring ich Hibernate das bei?


Kannst du über die Map vor dem speichern nicht einfach einen Bereinigung laufen lassen, die B3neu wieder durch B3aktuell ersetzt, wenn der Inhalt gleich ist?

_________________
Please rate useful posts.


Schauderhaft: Softwaredevelopment, Projectmanagement, Qualitymanagement and all things "schauderhaft"


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 11, 2008 3:39 am 
Newbie

Joined: Tue Jul 08, 2008 10:56 am
Posts: 8
Ja, das wäre das, was ich oben mit "manuelle Lösung" beschrieben habe.

Das bringt mich auf die Frage, ob es denn bei Hibernate die Möglichkeit gibt, eine callback-Methode zu definieren, die aufgerufen wird, bevor ein Objekt gespeichert wird?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 11, 2008 5:40 am 
Expert
Expert

Joined: Thu Jul 05, 2007 9:38 am
Posts: 287
ja, gibt es:

http://www.hibernate.org/hib_docs/core/ ... le/#events

Ich bin mir aber nicht sicher, was man zu diesem Zeitpunkt mit den Objekten noch machen darf. Also vorsichtig testen.

_________________
Please rate useful posts.


Schauderhaft: Softwaredevelopment, Projectmanagement, Qualitymanagement and all things "schauderhaft"


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 12, 2008 10:22 am 
Newbie

Joined: Tue Jul 08, 2008 10:56 am
Posts: 8
Hallo wieder,

die Diskussion hat mich zweifeln lassen, ob der ursprüngliche Entwurf wirklich der richtige ist. Am meisten überzeugend ist der Einwand, dass Hibernate die Domänenobjekte so persistieren soll, wie sie nun mal sind. Die neuen Werte, die anfangs leer sind, sind aber nur für die GUI nötig, und sollen nur, wenn sie gefüllt sind persistiert werden. Das bedeutet: wir füllen unser Domänenobjekt mit Daten, die die GUI benötig, das Domänenobjekt aber inkonsistent mit der Persistenzschicht macht. Also machen wirs jetzt anders und lassen die GUI die neuen Werte verwalten, setzen sie im Domänenobjekt, wenn sie wirklich dieses verändern sollen, und lassen Hibernate das Objekt dann normal persistieren.

Der Hinweis mit Events und Interceptors ist aber trotzdem sehr gut, denn ich denke manchmal muss man tatsächlich Domänenobjekte vorm Speichern "überarbeiten", auch wenn ich nach dieser Erfahrung in Zukunft immer genau überlegen werde, ob der Grund dafür kein unsauberes Design ist.

Vielen Dank für die Hilfe und die fruchtbare Diskussion,
Martin.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.