# JPA Entity im Client-Server-Umfeld



## tuxedo (14. Jan 2011)

Hallo zusammen,

ich frag mich gerade wie man das designtechnisch am besten löst:

Eine Client-Server-Anwendung (kein EE, kein EJB, ..., ist ein J2SE Server) arbeitet serverseitig mit JPA. Die Clients sollen die Daten (aufbereitet) anzeigen, verändern und weitere Daten hinzufügen können (CRUD eben).

Um eine Liste am Client anzuzeigen geht eine Anfrage an den Server der daraufhin seinen EntityManager bemüht und die relevanten Entities aus der DB holt.

So, aber was nun? 

Entities weiter an den Client schicken/serialisieren?
--> Der Client soll ja nix von JPA wissen. Er braucht eigentlich nur POJOs

Entities "umverpacken" in DTOs?
--> Code-Duplizierung: Die DTOs würde meist das selbe beinhalten wie die Entities, nur eben ohne Annotations etc. blabla

Prinzipiell scheue ich die DTOs nicht. Bin mir aber nicht sicher ob es der elegante Weg ist.

- Alex


----------



## maki (14. Jan 2011)

Solange der JPA Provider ByteCode manipulation beherrscht und beim Client keine Lazy Felder ausgelesen werden sollte es kein Problem sein, die Entities an den Client zu schicken, sind POJOs (mit Annotationen welche ignoriert werden).

Wichtig ist eben dass keine Lazyfelder ausgelesen werden sondernd die zu lesenden Attribute der Entities initialisiert sind.


----------



## tuxedo (14. Jan 2011)

Ja, sowas in der Art hatte ich mir gerade auch gedacht. 

Werde das ganze nur noch um Interfaces erweitern. Sprich, die Entities kriegen ihre Methoden via Interface diktiert. Und der Client arbeitet nur mit den Interfaces. Dann sollte das auch schön entkoppelt sein.

Nur müsste ich glaub ich die Entities from EntityManager detachen. Dann wäre das ganze wirklich sauber getrennt. Wenn ich das nicht detache: Hmm, ob das dann noch über, sagen wir RMI oder SIMON funktioniert?! Müsste ich ausprobieren. Aber vermutlich nicht. Denn für Entity-Objekte legen RMI und Co. ja keine Proxy/Remote-Objekte an. 

- Alex


----------



## maki (14. Jan 2011)

POJOs lassen sich auch gut serialisieren für den Austausch zwische Cleint/Server, Interfaces sind kein muss, aber wenn es sonst noch hilft.
Detachen und mergen sind dann standard aufgaben, ansonsten könnte es auch mit einem "Extended Persistence Context", da weiss ich aber zuwenig drüber.


----------



## tuxedo (14. Jan 2011)

Die Interfaces waren schon ne gute Idee. Nur kommen mir da glaub ich die Abhängigkeiten zwischen den Entities ins Spiel.

Wenn mich nicht alles täuscht, dann nutzt Hibernate nicht nur die annotierten Felder, sondern auch die dazugehörigen getter/setter (kann auch sein dass ich hier falsch liege).

Und wenn die Getter/Setter jetzt auf die Interfaces lauten, könnte sein dass Hibernate das nicht gebacken bekommt. Aber ich probiers einfach mal aus. 

Gruß und Danke mal soweit...
Alex

[update]

Okay, JPA kann auch mit Interfaces umgehen:


```
@OneToMany(mappedBy="service", targetEntity=EmServiceMethod.class)
    private List<IServiceMethod> methods = new ArrayList<IServiceMethod>();
```

Man muss nur die TargetEntity angeben, die die implementierung darstellt und schon kann man mit Interfaces arbeiten.


----------



## maki (14. Jan 2011)

Hibernate nutzt nicht die Setter wenn du die Attribute annotierst(Hibernate Annotations), die Annotation könnten in die Implementierung(wo sie imho hingehören), die Interfaces brauchen keine (JPA) Annotations und haben somit keine Abhängigkeiten zu Hibernate/JPA.


----------



## tuxedo (14. Jan 2011)

Ja, das hab ich jetzt auch gemerkt. Der Einsatz von Interfaces und das serialisieren und deserialisieren funzt wie gewünscht.

Zu den settern/gettern: Machen andere JPA Provider das auch so, oder macht das jeder wie er will?


----------



## maki (14. Jan 2011)

Das schreibt die EJB3 Spec. so vor


----------



## tuxedo (14. Jan 2011)

Ist die JPA 2.0 Spec Teil der EJB3 Spec? 

Weil ich nutzt kein JEE ...


----------



## maki (14. Jan 2011)

Genaugenommen ist JPA 1.0 Teil der EJB3 Spec., JPA 2.0 hat eine eigene bekommen, dieses Verhalten war aber schon für JPA 1.0 festgeschrieben, eben in der EJB3 Spec.


----------



## tuxedo (14. Feb 2011)

maki hat gesagt.:


> Detachen und mergen sind dann standard aufgaben, ...



Weißt du ob es hier irgendwelche Patters/Best Practices gibt?


----------



## tuxedo (23. Apr 2011)

Ich muss den Thread doch nochmal ausgraben...

Mit einfachen Entities funktioniert das recht gut:

Ein Client erfragt eine Entity, der EntityManger beschaft diese, detached sie und das Objekt wird zum Client serialisiert. Der kann dieses Objekt dann manipulieren und später dem Server wieder zukommen lassen. Und hier muss nun gemerged werden.

Das ist soweit erstmal nicht das Problem. Ich kann ja einfach alle Eigenschaften des Objekts vom Client auf eine "managed" Entity-Instanz übertragen. Aber was ist mit verketteten Entities?!

Sagen wir ich habe eine Benutzer-Entity die noch eine Liste mit BenutzerGruppen-Entities enthält (in der der Benutzer gemeldet ist). 

Da die Einträge in der BenutzerGruppen-Liste ja auch Entities sind, müsste ich wie folgt vorgehen:

Wenn der Server den Benutzer dem Client schicken will, muss er nicht nur den Benutzer detachen, sondern auch alle Benutzergruppen in der Liste.  
Wenn das Benutzer-Objekt vom Client dann verändert zurück an den Server geht, muss ich alle Benutzer-Eigenschaften an die attached Entity übertragen und muss durch die Liste mit Benutzergruppen iterieren und mir die IDs der Benutzergruppen beschaffen. Damit muss ich dann die Liste der Benutzergruppen der attached Benutzer-Entity abgleichen...

Das wird doch schnell aufwendig. Gibts hier bessere/einfachere Lösungen?

Gruß und schöne Ostern noch,
Alex


----------



## maki (23. Apr 2011)

> Aber was ist mit verketteten Entities?!


Sollte doch genauso funktionieren wenn ich mich recht erinnere :reflect:


----------



## tuxedo (23. Apr 2011)

maki hat gesagt.:


> Sollte doch genauso funktionieren wenn ich mich recht erinnere :reflect:



??

Okay, nochmal detailierter:

Ich hab in einer Entity-Klasse folgendes Feld:

```
@ManyToMany(targetEntity=EmUserGroup.class, mappedBy="users")
    private List<IUserGroup> groups = new ArrayList<IUserGroup>();
```

Wenn ich die Entity detachen will, muss ich doch theoretisch ach über "groups" iterieren und alle Gruppe-Entities noch detachen, oder?

Gut, weiter im Text: Eine Instanz dieser Entity detache ich, schicke sie zum Client, der modifiziert dieses und andere Felder. Die veränderte, detachte Entity wird dann zurück zum Server geschickt. Ein "entityManager.attache()" gibt es nicht. "merge()" geht nur für Entities die attached sind.  Also bleibt mir doch nix anderes übrig als "groups" im immernoch detachten Objekt durchzuiterieren und alle Gruppen a) abzugleichen und mit attachten Entities zu "ersetzen". 

- Alex

[update]

Mist, noch ein Problem. In der Interface-Sache hab ich noch nen dicken Denk-Fehler... Der Client kann kein Objekt deserialisieren dessen Klasse er nicht hat. Ein Interface reicht ihm ja nicht. Hmmpf.


----------



## tuxedo (25. Apr 2011)

[update2]

Das mit dem Seralisieren der Entities geht noch weiter: Der Client benötigt nicht nur die Implementierung der Entity um diese deserialisieren zu können. Natürlich braucht er auch noch die verwendeten Annotation-Klassen in der Entity. Ergo: Der Client braucht nicht nur die Entity-Klasse, sondern auch noch JPA .... *mist* Hätt' ich mir gleich denken können.
Jetzt ist der Weg zum DTO nicht mehr allzuweit. Und das wo ich doch extra DTOs vermeiden wollte. 
Überall im Netz liest man ja dass es Blödsinn wäre DTO einzusetzen wenn man schon JPA Entities hat. Sind ja "nur" POJOs. Leuchtet ja auch alles ein. Aber nirgendwo steht, dass auf der anderen Seite wieder JPA Interfaces und Co. vorausgesetzt wird... Und für einen schlanken Client, der sich eigentlich gar nicht dafür interessiert wie der Server die Dinge speichert oder was dort annotiert ist, ist das "ganz schön heftig" wie ich finde. 

Nun ja. Die Entity-Implementierung dem Client zu geben wäre ja nicht so verkehrt (sind ja nur POJOs). Aber der "Rattenschwanz" den die JPA Annotations an Dependency noch mit sich zieht ist unschön. Also entweder zurück zur Definition via XML, oder halt doch ganz weg von JPA und iBatis und Co. benutzen und dort Code von Config strikt trennen.

Gruß
Alex


----------



## maki (25. Apr 2011)

> Wenn ich die Entity detachen will, muss ich doch theoretisch ach über "groups" iterieren und alle Gruppe-Entities noch detachen, oder?


Das wäre eine Möglichkeit.
Fetch Joins/queries sollten da aber geeigneter sein.
Interessant wäre das Mapping der "owning side", die Cascade zB.



> Gut, weiter im Text: Eine Instanz dieser Entity detache ich, schicke sie zum Client, der modifiziert dieses und andere Felder. Die veränderte, detachte Entity wird dann zurück zum Server geschickt. Ein "entityManager.attache()" gibt es nicht.
> "merge()" geht nur für Entities die attached sind. Also bleibt mir doch nix anderes übrig als "groups" im immernoch detachten Objekt durchzuiterieren und alle Gruppen a) abzugleichen und mit attachten Entities zu "ersetzen".


merge attached die Entites wieder, ist schon das richtige.



> Nun ja. Die Entity-Implementierung dem Client zu geben wäre ja nicht so verkehrt (sind ja nur POJOs). Aber der "Rattenschwanz" den die JPA Annotations an Dependency noch mit sich zieht ist unschön. Also entweder zurück zur Definition via XML, oder halt doch ganz weg von JPA und iBatis und Co. benutzen und dort Code von Config strikt trennen.


Anstatt den Client die Annotierten Entites direkt referenzieren zu lassen, kann der Client doch auch Interfaces (ohne Annot.)  referenzieren.
Jedenfalls ist Hibernate imho sehr invasiv, ist aber auch länger her dass ich damit wirklich gearbeitet hätte.
Vielleciht hilft ja das bytecode enhancement etwas.
Man kann übrigens mit Hiebrate bzw JPA das Mapping auch in XML Dateien haben.


----------



## tuxedo (26. Apr 2011)

> Fetch Joins/queries sollten da aber geeigneter sein.



*Bahnhof* Kannst du mir hier nen extra Informationsbrocken hinwerfen damit ich google damit füttern kann?



> Anstatt den Client die Annotierten Entites direkt referenzieren zu lassen, kann der Client doch auch Interfaces (ohne Annot.) referenzieren.



Ja, das war ja auch mein ertsre Gedanke. Deshalb haben alle Entities auch ein eigenes Interface bekommen. Die Annotations stehen nur in der Implementierung. Aber wenn ich eine Entity auf Serverseite serialisiere (klassische Java serialisierung), und auf Clientseite deserialisiere, dann braucht der Deserialisierungsmechanismus die Implementierung. Da reicht das Interface alleine nicht aus. Und genau hier liegt der Hund begraben: Ich will dem Client nicht auch noch die Implementierung geben. Zumindest nicht dann, wenn da noch fetzen von JPA/Hibernate drin zu finden sind



> Jedenfalls ist Hibernate imho sehr invasiv, ist aber auch länger her dass ich damit wirklich gearbeitet hätte.



Eben das ist das Problem. Der Client braucht zum deserialisieren mindestens die Implementierung. Und die beinhaltet Annotations. Und deshalb braucht der Client auch noch JPA Abhängigkeiten. Und das ist nicht nur unschön, sondern auch unnötig. Denn dem Client ist es wurscht wie der Server die Daten speichert. 




> Vielleciht hilft ja das bytecode enhancement etwas.



War das generell gemeint, oder gibts da ein JPA/Hibernate feature?! Aber wie dem auch sei: Hört sich nach weiterer komplexität an. 



> Man kann übrigens mit Hiebrate bzw JPA das Mapping auch in XML Dateien haben.



Ja, über die orm.xml ... Da bin ich auch gerade am überlegen ob ich die JPA Infos dahin auslagere. Dann hätte ich wieder "saubere" Entity POJOs. Der Client bräuchte dann kein JPA/HIbernate beim deserialisieren. 
Aber im Fall von JPA bin ich mir nicht sicher ob das wirklich zukunft hat. Geht doch alles in Richtung Annotations. Für mich sieht's jetzt schon so aus als würde die orm.xml eher eine Nischenfunktion erfüllen die nur noch wenige benutzen, immer schlechter dokumentiert ist und bald wegfällt (okay, dauert wohl ne weile bis JPA3 da ist...). Hat halt irgendwie so ein "obsolete" Beigeschmack... Die Frage ist dann: Gibts nicht andere Lösungen wo genau das erwünscht ist?!


----------



## maki (26. Apr 2011)

> *Bahnhof* Kannst du mir hier nen extra Informationsbrocken hinwerfen damit ich google damit füttern kann?


Klar: Chapter 14. HQL: The Hibernate Query Language

Deine Idee mit dem drüberitererieren läuft doch darauf hinaus, dass die Lazy Attribute geladen werden, nachträglich.
Mit einem Fetch Join (so nennt das Hibernate) kannst du das in einem einzigen Statement erledigen lassen.



> Ja, das war ja auch mein ertsre Gedanke. Deshalb haben alle Entities auch ein eigenes Interface bekommen. Die Annotations stehen nur in der Implementierung. Aber wenn ich eine Entity auf Serverseite serialisiere (klassische Java serialisierung), und auf Clientseite deserialisiere, dann braucht der Deserialisierungsmechanismus die Implementierung. Da reicht das Interface alleine nicht aus. Und genau hier liegt der Hund begraben: Ich will dem Client nicht auch noch die Implementierung geben. Zumindest nicht dann, wenn da noch fetzen von JPA/Hibernate drin zu finden sind


Die Annotation sollten kein Problem darstellen IMHO, könnte mich da aber täschen.
Problemtisch ist, wenn die Entities Hibernate Code direkt enthalten. Das ist zB. der Fall, wenn man den standard Hibernate Weg geht, da werden dyn. Proxies zur Laufzeit erstellt, mit Referenzen zu Hibernate Bibliotheken.
Mit dem byte code enhancement werden zur Laufzeit keine dyn. Proxies erstellt, sondern der Bytecode der Entities wird abgeändert, ist schneller und weniger Abhängigkeiten zur Laufzeit.



> War das generell gemeint, oder gibts da ein JPA/Hibernate feature?! Aber wie dem auch sei: Hört sich nach weiterer komplexität an.


Gar nciht kompliziert, nur ein weiterer Schritt im Buildprozess, wenn man zB. Maven einsetzt ist das sehr schnell erledigt.



> Ja, über die orm.xml ... Da bin ich auch gerade am überlegen ob ich die JPA Infos dahin auslagere. Dann hätte ich wieder "saubere" Entity POJOs. Der Client bräuchte dann kein JPA/HIbernate beim deserialisieren.
> Aber im Fall von JPA bin ich mir nicht sicher ob das wirklich zukunft hat. Geht doch alles in Richtung Annotations. Für mich sieht's jetzt schon so aus als würde die orm.xml eher eine Nischenfunktion erfüllen die nur noch wenige benutzen, immer schlechter dokumentiert ist und bald wegfällt (okay, dauert wohl ne weile bis JPA3 da ist...). Hat halt irgendwie so ein "obsolete" Beigeschmack... Die Frage ist dann: Gibts nicht andere Lösungen wo genau das erwünscht ist?!


IMHO kann man JPA Annotatinen in kleineren Projekten gut nutzen, wenn man allerdings irgendwann man mehr JPA Annotationen als Javacode in der Klasse hat, wählt man recht schnell die XML Hölle gegenüber der Annotations Hölle.
Wüsste jetzt ehrlich gesagt nciht dass die orm.xml irgendwann wegfallen sollte.


----------



## tuxedo (26. Apr 2011)

> Die Annotation sollten kein Problem darstellen IMHO, könnte mich da aber täschen.



Habs ausprobiert. Zum deserialisieren braucht man die Klassen zu den Annotations. hab keine Hibernate-spezifischen Annotations verwendet. Nur reines JPA. Hier würde als eine Dependency zu JPA reichen. Aber auch das möchte ich vermeiden. 



> Problemtisch ist, wenn die Entities Hibernate Code direkt enthalten. Das ist zB. der Fall, wenn man den standard Hibernate Weg geht, da werden dyn. Proxies zur Laufzeit erstellt, mit Referenzen zu Hibernate Bibliotheken.
> Mit dem byte code enhancement werden zur Laufzeit keine dyn. Proxies erstellt, sondern der Bytecode der Entities wird abgeändert, ist schneller und weniger Abhängigkeiten zur Laufzeit.



Muss ich mir mal genauer anschauen. Gehe bis jetzt den standard JPA Weg.



> IMHO kann man JPA Annotatinen in kleineren Projekten gut nutzen, wenn man allerdings irgendwann man mehr JPA Annotationen als Javacode in der Klasse hat, wählt man recht schnell die XML Hölle gegenüber der Annotations Hölle.
> Wüsste jetzt ehrlich gesagt nciht dass die orm.xml irgendwann wegfallen sollte.



Hmm, hab bisher nur gehört/gelesen dass Annotations der "optimale" und "bessere" Weg wären. Als Begründung wurde genannt, dass man gleich auf einmal sieht wie beides zusammenhängt: Code + Config. Leuchtet ja auch ein. Aber mehr wie "kleine" Projekte findet man in Tutorials wenig und Erfahrungsberichte zu großen Projekten sind mir noch nicht untergekommen. Deshalb dieser seltsame Beigeschmack nun alles in die ORM.xml auszulagern... Aber gut, wenn es da kein riesiges Gegenargument gibt, wäre das wie gesagt auch eine Lösung.

Wäre nur eben interessant gewesen ob es nicht noch bessere Libs als JPA/HIbernate gibt bei denen da besonderes Augenmerk drauf gelegt wird, Code von Config zu trennen. So Spontan würde mir hier nur iBatis einfallen.


----------



## maki (26. Apr 2011)

> Hmm, hab bisher nur gehört/gelesen dass Annotations der "optimale" und "bessere" Weg wären. Als Begründung wurde genannt, dass man gleich auf einmal sieht wie beides zusammenhängt: Code + Config. Leuchtet ja auch ein. Aber mehr wie "kleine" Projekte findet man in Tutorials wenig und Erfahrungsberichte zu großen Projekten sind mir noch nicht untergekommen. Deshalb dieser seltsame Beigeschmack nun alles in die ORM.xml auszulagern... Aber gut, wenn es da kein riesiges Gegenargument gibt, wäre das wie gesagt auch eine Lösung.


Es gab anfangs schon rufe dass Annotationen den Gedanken des SoC ad absurdum führen... wie gesagt, wenn man JPA/Hibernate Annotationen hat, dann noch welche für die Validierung, dann noch andere (Spring/Guice) etc., kommt einem der Gedanke der orm.xml nicht mehr abwegig vor.
Kannst ja mal nach "Annotation Hell" googeln.


> Wäre nur eben interessant gewesen ob es nicht noch bessere Libs als JPA/HIbernate gibt bei denen da besonderes Augenmerk drauf gelegt wird, Code von Config zu trennen. So Spontan würde mir hier nur iBatis einfallen.


Kannst dir ja mal JDO ansehen, DataNuclues liefert die Implementierung dazu.
Ünterstützt übrigens auch Annotationen oder XML Konfig.
Das volle Potenzial kann man bei JDO nur mit XMl Konfigs erreichen (glaube ich), JDO ist im Gegensatz zu JPA nicht nur für RDBMS gedacht (einer der Gründe warum Oracle u. IBM für JPA und gegen JDO waren).
JDO ist aber heute nur noch eine Nische, wenn man mal von der Google App Engine absieht, denn die setzt auf JDO & DataNuclues, aber nicht auf RDBMS.

Alternativ wären DTOs auch eine Option, ist zwar aufwändiger, aber dafür flexibler.


----------

