JPApi #
Soll eine Bibliothek für eine "Java Persistenz API" werden, die flach und einfach ist,
Es kann losgehen, ich brauche nun eine Abstraktion, für mich privat (Homepage) und für ein paar Freunde. (JensKapitza)
Nach einer Besprechung beim LUG-Treffen wollten wir dieses Thema mal wieder aufwärmen.
JPApi.Lizenz | GPL. (U.u. BSD, LGPL) |
Projektname | JProxyApi, SwingingBeans |
SVN | http://svn.bayen.mine.nu/svn/tbayen/trunk/SwingingBeans/ |
Entwickler | JensKapitza, ThomasBayen,wer noch? |
OpenJPA | hmm muss man sich mal näher angucken |
Probleme | Lösung |
---|---|
UTF-8, ich bin doch ISO nutzer ;) Umlaute sind schön! | Ist im Projekt jetzt fest eingestellt, bitte updaten |
Projektname #
Also den Projektnamen JPApi nehme ich nicht. Der ist ja hässlich. Da werden wir im Limericks nochmal das eine oder andere Guinness drüberlaufen lassen müssen. :-) Ein öffentliches SVN habe ich bei mir. Wenn das Projekt in Fahrt kommt, können wir es auch nach Sourceforge transferieren (in lug-kr oder einem eigenen Projekt).
- Wir benutzen mein bestehendes Projekt "SwingingBeans". Der Name ist schön und witzig, mein SVN etc. steht schon. Meine bisherigen Persistenz-Klassen befinden sich dort bereits. Der "Haken" ist, daß ich meine Swing-Visualisierung (die auf der Persistenzschicht aufbaut) mit in diesem Projekt habe (und weiter haben werde). Natürlich sollten wir unterschiedliche Pakete benutzen und wir können im Build-Skript natürlich auch gerne getrennte JAR-Dateien erzeugen. Einer späteren Abspaltung in ein eigenes Projekt steht nichts im Wege (z.B. wenn uns ein anderer, besserer Name eingefallen ist). Diese Methode ist schnell und unkompliziert und ist im Moment mein Favorit. Siehe http://swingingbeans.javaproject.de/ -- ThomasBayen
- Phaseolus: persistent holding and saving of entity objects - light, usable and secure. Siehe http://de.wikipedia.org/wiki/Phaseolus_vulgaris -- ThomasBayen
- Perrijer - Persistenz vom Jenz :-)
- S.O.B. - Save Our Beans
Ziele, die man erreichen sollte #
- kompatibel mit JDK >= 1.5
- wenige bis hin zu gar keinen Abhängigkeiten
- Möglichst wenig Konfiguration
- Annotationen an der vorhandenen API von Sun orientieren (EJB)
- unsere API an der API von Sun orientieren (?)
- kurze Ladezeit (im Vergleich z.B. zu Hibernate)
- ClassLoader, Interfaces und Klassen auch drüber hinaus aus lesbar machen (Konfigurations Klasse, * addClass() *)
- so weit wie möglich Nutzung vorhandener APIs (z.B. BeanInfo, PropertyDescriptor, Collections wie List oder Set, etc.)
- Laszy Listen, Sets, ...
- SQL als Abfrage sprache
- Object Cache, nur änderungen in Datenbank speichern
- Treiber unabhängig, eigene Driver.class als Proxy nutzen
- reconnect, oder STATEMENTS doppelt absetzen bei einem Ping timeout
- Funktionsfähigkeit in JavaSE und in JavaEE
- automatische Erstellung von neuen Datenbanken
- automatisierte Erzeugung von Swing-Widgets zur Bearbeitung von Daten
Features #
- Mapping
- Caching (PREPARED Statement)
- Connection POOL
- Konfiguration durch Java Quellcode, Konventionen, Annotationen, Konfigurationsdateien, vorhandene Datenbank (in dieser Reihenfolge)
- Im Normalfall stehen alle Eigenschaften einer Bean in einer einzigen Datei (im Java-Quelltext)
- automatische Erstellung von GUIs (Web und Swing)
- Arbeit mit mehreren Datenbanken gleichzeitig (mit übergreifenden Fremdschlüsseln)
- Caching von Metainformationen
Observer-Pattern (Wikipedia) #
- Änderungen werden direkt in der Datenbank gemacht
- Die Observer-Funktionalität sollte automatisch in vorhandene Beans eingebaut werden (läuft übrigens bereits bei ThomasBayen mit BCEL)
- BCEL (http://bcel.apache.org) - Bytecode-Manipulation, wird von vielen bekannten Projekten genutzt
- SERP (http://serp.sf.net) - Alternative zu BCEL
- Jasmin (http://jasmin.sf.net) - Bytecode-Assembler
- AspectJ (http://www.eclipse.org/aspectj) - aspektorierentiertes Java
- Proxy-Objekte (http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/Proxy.html) - Lösung aus dem JDK
- abgeleitete Klassen on the fly erzeugen. (Kann man in Java6 einfach einen Compiler aufrufen?) (siehe auch Dynamic Java Compiler)
- das sollte mit Janino gehen. -PeterHormanns
- Mit Exorcist könnte man zur Compile-Zeit erweiterte Klassen erzeugen (benutzt SERP)
- abgeleitete Klassen in einer Scriptsprache erzeugen (geht mit Rhino, das in Java6 enthalten ist oder auch mit Groovy).
- http://menzsoft.ch/Javassist.pdf bzw. http://jdj.sys-con.com/read/38672.htm - es scheint das zu sein wass ich immer gesucht habe ;) -- JensKapitza
- Javassist
- Compilieren von Java-Quellcode mittels der Java Compiler API
- Es sollte möglich sein, den Observer auch selbst zu implementieren (ganz ohne BCEL) - das macht dessen Funktionsweise transparenter.
Was nimmt man für die Modifikation der Beans #
- Die o.g. Bytecodegeneratoren können unser Problem wohl alle lösen. Allerdings ist die Codeerzeugung alles andere als intuitiv und einfach. Andererseits kann man durch Delegation dazu kommen, daß man die eigentliche Funktionalität wieder in richtigem Java schreiben kann. Man kann die persistente Klasse als Ableitung der Original-Beanklasse implementieren. Wenn diese Lösung einmal steht (ich habe es bereits mit BCEL gemacht), ist sie eigentlich ganz gut.
- Proxy-Objekte bauen auf einem Interface statt auf einer Bean-Class auf. Man müsste also zu jeder Beanklasse ein identisches Interface haben. Vielleicht erstellt man dieses per BCEL? Es entsteht dann das Problem, daß das Proxy-Objekt nicht mehr die Klasse der ursprünglichen Bean hat. Das kann zu Verwirrung führen. Hibernate scheint so zu arbeiten, da mir dort genau dieses Problem aufgefallen ist.
- AspectJ ist garantiert die eleganteste und intuitivste Lösung. Scheinbar kann man es neuerdings sogar dazu bringen, in einer Standard-Umgebung zu laufen (ohne Precompiler). Es erlaubt aber leider nicht, zusätzliche Methoden in einer Klasse zu erzeugen. Es modifiziert immer die Originalklasse, d.h. auch nicht-persistente Instanzen der Klasse werden verändert.
Da ja leider in Java kein Java-Compiler enthalten ist, aber in Java6 Rhino (Javascript Engine) enthalten ist, ist das vielleicht die richtige Sprache. Java als Sprache hätte den Vorteil, daß die o.g. angepassten Observer ganz normal in den Sourcecodebaum kommen könnten.
Ein Kriterium könnte noch sein, ob es unter JavaWebStart ohne weitere Sicherheitsfreigabe lauffähig ist (wäre bei Java einfach, bei Rhino müsste man das mal testen).
Ach ja - meine BCEL-Lösung ist eine Ableitung der Beanklasse, um die es geht, die ein besonderes Interface implementiert. Hibernate hingegen benutzt Proxy-Objekte. Mir ist der Sinn der Proxy-Lösung noch nicht aufgefallen. Im Gegenteil haben die persistenten Objekte dann ja eine komplett andere Klasse. Kennt jemand ein Argument für Proxys?
Problem mit der Umwandlung der Objektklasse
Ich habe ein Problem gefunden, das ich spontan nicht lösen kann. Laut JPA kann man ein Objekt mit
Player p = new Player();
erzeugen und dann mit
EntityManager em = ... em.persist(p);
persistent machen. Das verstehe ich nicht ganz. Vorher ist p ja wohl einwandfrei ein ganz normales Player-Objekt ohne irgendwelche Besonderheiten. Nach dem persist-Aufruf müsste es doch dann ein persistentes Spezialobjekt (also eine Ableitung, ein Proxy oder sowas) sein. Wie kann denn die Methode em.persist die Klasse von p verändern?!?
Wahlurne #
Vielleicht ist es langsam an der Zeit, einige Threads zusammenzufassen und ein paar grundlegende Entscheidungen zu treffen:
Wie werden Observer in die Klassenstruktur eingebaut?
- Als abgeleitete Klassen der Basis-Beanklasse
- Als Proxy-Objekt (wie z.B. in Hibernate)
- Als Modifikation der Basis-Beanklasse (wie z.B. in AspectJ)
Was benutzen wir zur Erzeugung von Observern?
- Erzeugung von Java-Quelltext (benötigt eingebetteten Java-Compiler)
- Erzeugung von Javascript (benötigt Java6, das Rhino enthält)
- Erzeugung von Groovy-Quelltext (benötigt Java6 und groovy)
- Erzeugung der Klassen direkt mit BCEL (benötigt BCEL-Bibliothek)
Design-Entscheidungen #
Hier möchte ich kontroverse Dinge sammeln bzw. Entscheidungen dokumentieren, die nicht sofort offensichtlich sind.
Wie direkt ist die Verbindung zur Datenbank? #
Eine Möglichkeit ist es, unabhängige Beans (mit Fremdschlüsseln ggf. einen Beanbaum) zu laden. Diese werden dann frei bearbeitet und werden mit einer update-Methode wieder zurückgeschrieben. Das andere Extrem ist es, wenn jeder getter und setter der Bean direkt auf einen SQL-Befehl gemappt wird. Zwischenlösungen sind denkbar. Für verschiedene Probleme sind verschiedene Bindungsstärken sinnvoll. Was sollte nun wirklich implementiert werden?
Nach Lesen der JPA-Spezifikation habe ich bemerkt, daß das ein Problem der Datenbank-Transaktion ist. In einer JavaEE-Umgebung ist das ganze wohl egal, in JavaSE sollte man entweder eine Möglichkeit haben, die Transaktion auf "autocommit" zu stellen, oder in der GUI darauf achten, daß nach jedem Klick brav committet wird. Was besser ist, bin ich noch unentschlossen.
Was ist eine Bean? #
Eine Klasse mit gettern und settern, um auf bestimmte Eigenschaften zuzugreifen. Beispiel:
public class Bean { private Feld a; public Feld getA() { return a; } public setA(Feld b) { a = b; } }
Klassenstruktur #
Ich denke, wir sollten damit anfangen, die Teile der JPA-Spezifikation (JSR 220 lesen!) zu implementieren, die wir für unsere konkreten Anwendungen benötigen.
Sind wir schon soweit, daß wir eine grobe Klassenstruktur angeben können und dann vielleicht auch schon Aufgaben verteilen? Im Moment fällt es mir noch schwer, das Problem zu fassen und in zwei oder mehr Teile zu zerlegen.
Implementation von Collections #
Jens schlug vor, als Basis einer Collection das ResultSet zu nehmen. Leider steht - wie ich vermutete - in der ResultSet API, daß die Methoden zur Navigation in ResultSets eine SQLFeatureNotSupportedException werfen dürfen, wenn sie vom JDBC-Treiber nicht unterstützt werden.
Apropos cachen sinnvoll: Es bleibt der Unterschied, daß ein ResultSet IMO seinen Inhalt nicht ändert, ein Query bei jedem Zugriff anders aussehen kann. Beides kann sinnvoll sein. Was passiert denn mit einem ResultSet, wenn die Transaktion abgeschlossen ist? Wenn man das RS-Objekt stundenlang behält, hat man dann immer noch Zugriff auf alle alten Daten?!? Ist das irgendwo spezifiziert? -- ThomasBayen
Diskussion #
JSP EJB und alles andere NEWS oder keine ahnung wohin damit#
Links #
- Hauptseite zur Java Persistence API von Sun
- offizielle FAQ zu JPA von Sun
- Artikel von Sun über JPA
- Artikel von Sun zu JPA unter Java2SE
- JPA-Implementation von Glassfish
- BeanKeeper oder PBeans scheinen mir ein leichtgewichtiger Ansatz, wenn es nicht Hibernate oder JPA sein soll... --PeterHormanns