# ORM-Persistenz & Webservices: Datenabhängigkeit?



## serethos (27. Mai 2009)

Mein Problem ist themenübergreifend, aber ich hoffe, dass es gut in dieser Kategorie aufgehoben ist.

Generell geht es mir um das Problem, dass eine Persistenzschicht (in meinem Beispiel aufbauend auf Hibernate) andere Anforderungen an das Verwalten von Objektabhängigkeitn besitzt, als die Service-Schicht (hier: SOAP via CXF).

Als Einleitung mal ein einfaches Datenmodell für eine klassische Webanwendung.
Code:

Kunde  --1-------n-- Bestellung --n-------n-- Artikel

Abgebildet wird also eine Entität Kunde, welche mehrfach bestellen kann, jede Bestellung gehört eindeutig zu einem Kunden und besteht aus mehreren Artikeln. Jeder Artikel kann natürlich Bestandteil verschiedener Bestellungen sein.

Dieses Datenmodell wird nun über Hibernate entsprechend umgesetzt, auch die Relationen werden im Mapping berücksichtigt.
Via CXF wird nun ein Set an Soap-Services zur Verfügung gestellt:
Code:

```
// Liste aller Kunden
List<Kunde> listKunden()                 

// alle Bestellungen eines bestimmten Kunden
List<Bestellung> getBestellungen(KundenID id)

// Liste aller Artikel
List<Artikel> listArtikel()
```
Das Hibernate-Mapping erlaubt das Laden abhängiger Objekte, was häufig, aber nicht immer gewünscht sein kann. Soll z.B. eine Liste aller Kunden abgerufen werden (listKunden()), so würden die referenzierten Bestellungen mitgeladen und via Webservice verschickt werden. Wollte der Aufrufer allerdings nur die Liste der Kunden haben - ohne Bestellungen - dann ist es zusätzlicher Overhead.

Die (vermeintlichen) Lösungen, die ich sehe sind:

*Lazy Loading*
Kann aktiviert werden. Hier würde das Service-Framework aber versuchen, die abhängigen Objekte nachzuladen, soabald die Kunden serialisiert werden (und es würde dank der bereits geschlossenen Session knallen).

*Lazy Loading + Null setzen*
Lazy Loading aktivieren (für alle Fälle, in denen es vielleicht gebraucht wird) und eine ServiceMethod anbieten, welche die Referenzen = null setzt, damit sie nicht nachgeladen werden.
Halte ich für sehr schmutzig, wartungsunfreundlich, außerdem muss sich der Aufrufende auch bewußt sein, dass er leere Referenzen erhält, die keine Aussagekraft über tatsächliche Abhängigkeiten haben.

*Service mit eigenem Model*
Hier würde ein eigener Service mit verändertem Model benutzt, etwa:
Code:

```
List<KundeOhneBestellung> listKunden2()
```
Dieses Model würde keine Referenzierung zu den Bestellungen enthalten. Der Aufrufende würde genau das bekommen, was er verlangt hat, eine technische Besonderheit gäbe es auch nicht. Für alle möglichen Fälle ein eigenes Model zu nutzen bedeutet Unübersichtlichkeit und viel zusätzlichen Aufwand (Pflege der Models, Persistenzcode etc)

*Keine referentielle Abhängigkeit*
Es wird auf das referentielle Mapping der Entitäten verzichtet. Das bedeutet, alle Objekte können nur isoliert geladen werden. Vorteile wie kaskadierendes Löschen, Laden von Objektbäumen, referentielle Integrität fallen weg. Auch kein Ausweg.

Grade in komplexerem Datenmodellen und vielfachen Anforderungen an das Laden und Verbreiten der Daten in der Business-Schicht und über die Services nimmt dieses Problem an Bedeutung zu. Allerdings dürfte es so ziemlich jede verteilte Anwendung betreffen, die in die gleiche Kategorie fällt ..

Wie wird hier für gewöhnlich mit umgegeangen?
Ich bin für jede Anregung dankbar!


----------



## maki (27. Mai 2009)

HQL bietet joined fetch queries, damit werden lazy attribute/collections gleich mitgeladen, pro use case eine dieser queries, dann noch per use case eine service Methode die dann die richtige query aufruft, oder aber eine einzige service methode mit einem zusätzlichen parameter der beschreibt was alles mitgeladen weden soll.


----------



## byte (27. Mai 2009)

In diesem Fall würde ich wohl nur DTOs über die Webservices bereitstellen (also ein eigenes Modell). Meiner Erfahrung nach werden Webservices sowieso gähnend langsam, wenn man mit komplexen Objektgraphen arbeitet. Und eine Lösung, um mit CXF bidirektionale Referenzierungen abzubilden, habe ich bisher auch noch nicht gefunden (falls da jemand die Lösung kennt, bitte her damit).


----------



## serethos (27. Mai 2009)

Danke für die Antworten.

Das programmatische setzen des FetchMode ist natürlich wesentlich eleganter als mein Vorschlag, die Referenz = null zu setzen. Trotzdem bleibt das Problem, dass die Models (oder TransferObjects) stark kontextabhängig genutzt werden müssen. D.h. der Aufrufende darf nicht in Versuchung geraten, doch Abhängigkeiten nutzen zu wollen, die laut Datentyp zur Verfügung stehen, aber beim Serviceaufruf nicht mitgeladen wurden. Sicher muss die Nutzung einer Service-API immer bewußt passieren, allerdings sehe ich es als sehr benutzerunfreundlich und verwirrend an, Models zu nutzen, welche Properties mit sich schleifen, die je nach Situation nicht genutzt werden dürfen. 
Dies fällt insbesondere ins Gewicht, wenn die Service-API erlaubt, ein erworbenes Objekt ohne Referenzen wieder zurückzuschreiben, also etwa:


```
// client
Set<Kunde> kunden = service.getKundenOhneBestellungsReferenzen();
kunde.get(0).addBestellung(new Bestellung(..));
service.setKunde(kunde.get(0));
```

Hier serverseitig muss extra gegenggeprüft werden, ob der Kunde überhaupt hätte hinzugefügt werden dürfen (Das Set könnte ihn ja bereits enthalten). 
Ein ähnlicher Fall, bei dem Attribute eines Models zwar beim Abfragen, aber nicht beim Zurückschreiben gewünscht sind, wäre der (unveränderliche) Username eines Kundenobjekt:


```
// client
Kunde k = service.getKunde(kundenID);
k.setUserName(...);
service.setKunde(k);

// server
saveOrUpdate(k); // darf nicht passieren, denn der Username ist illegal verändert  worden
```

In diesem Fall muss serverseitig der Username extra validiert werden - das ist bei vielen read-online Attributen Zusatzaufwand und muss im Contract der Service-API genau geklärt werden. Rein aus dieser Sicht wäre ein write-Model, welches erst gar keinen Usernamen enthält, logischer.

Die Nutzung von x-Models für lesenden oder schreibenden Zugriff bzw mit und ohne gewisse Referenzen, eventuell auch noch unterschiedliche für Persistenz und Service artet hingegen in einem unwartbaren Wust an ähnlichen Klassen aus.


----------



## byte (27. Mai 2009)

Vollkommen richtig. Ich würde nur dann die Hibernate Objekte direkt über den Service nach draussen geben, wenn man dem Klienten vollkommen vertrauen kann, sprich wenn man selbst den Client implementiert. Nur dann weiss man, was man machen darf und was nicht.

Wenn Du hingegen dem Nutzer des Services nicht vertrauen kannst, dann sind Usecase-spezifische Transfer Objekte die sichere Alternative.


----------

