O/R-Mapping mit ObJectRelationalBridge aus dem Jakarta-Projekt

Einfach Speichern

Sven Haiges

Benutzen Sie bereits ein O/R-Mapping Tool? Nein? Das Open Source-Projekt ObJectRelationalBridge (OJB) erlaubt es Ihnen, beliebige Java-Klassen auf einfache Art und Weise in relationalen Datenbanken zu speichern. In diesem Artikel wird zunächst auf die Grundidee des objektrelationalen Mappings eingegangen. Es folgen die Vorstellung des O/R-Mapping Tools OJB aus dem Jakarta-Projekt sowie eine kurze Demo-Applikation zum einfachen Nachvollziehen der Arbeitsweise von OJB. Ein Interview mit Thomas Mahler, Mitglied des OJB Project Teams, soll Ihnen Lust auf eigene Experimente mit diesem Tool machen.

Objektrelationales Mapping

Die Kunst des objektrelationalen Mappings besteht darin, den Zustand (die Attribute) einer Java-Klasse in die Spalten einer relationalen Datenbanktable zu übertragen. Stellen Sie sich eine JavaBean-Klasse Adresse vor, welche die Properties Straße, Hausnummer, PLZ und Ort hat. In diesem einfachen Beispiel können die Properties der Klasse direkt in die Spalten einer Datenbanktable mit den gleichlautenden Spaltennamen übertragen. Als Tabellenname kann einfach der Name der Java-Klasse benutzt werden. Ein O/R-Tool hat somit die Aufgabe, für den Benutzer transparent die Speicherung und die Wiederherstellung von Java-Klassen aus und in relationale Datenbanken vorzunehmen. Ein solches Tool ist die ObjectRelationalBridge aus dem Jakarta-Projekt – kurz OJB.

Sie können sich sicher vorstellen, dass es nicht immer so einfach wie im obigen Beispiel funktioniert. Wie werden beispielsweise Polymorphismus oder Referenzen zu anderen Klassen abgepictureet? Zunächst aber zu den Vor- und Nachteilen des O/R-Mappings allgemein, um Ihnen die Entscheidung, ein solches Tool benutzen zu wollen oder nicht, einfacher zu machen. Die Vorteile des O/R-Mappings sind:

  • Die komplette SQL-Generierung wird vom O/R-Tool übernommen. Sie schreiben kein SQL mehr und vermindern somit auch drastisch die Anzahl der Testläufe, um sicherzustellen, dass Ihre eigene Persistenz-Lösung funktioniert. Ihre Anwendung wird stabiler.
  • Die Entwicklungszeit reduziert sich nach der einmaligen Einarbeitung drastisch. Sobald Sie die Grundidee Ihres O/R-Tools kennen gelernt haben, können Sie sehr schnell und fehlerfrei entwickeln. Der einmalige Aufwand zahlt sich bereits mit dem zweiten Projekt aus.
  • Sie werden unabhängiger von der benutzten Datenbank. Viele O/R-Mapping Tools unterstützen die unterschiedlichsten Datenbanken, angefangen bei MySQL bis zu einer Oracle Datenbank oder IBM DB2. Die Umsetzung von Aktionen wie INSERT oder SELECT geschieht im O/R-Tool, welches für die jeweilige Datenbank die passenden SQL-Dialekte auf Lager hat. Wechseln Sie die Datenbank, dann geben Sie Ihrem O/R-Tool einfach über die Konfiguration Bescheid und übertragen die Daten.

Alles hat seine Nachteile, leider auch das O/R-Mapping:

  • Performance. Ihr eigener SQL-Code kann höchst speziell auf die Datenbank angepasst werden, somit erreichen Sie höchste Performance.
  • Sie verlernen die SQL nicht.

Die Performance einer Anwendung ist oft eine kritische, nicht-funktionale Anforderung. Wer wartet schon gerne eine Minute auf seine Suchanfrage? Jedoch bieten die O/R-Mapping Tools auch hier eine oft akzeptable Lösung: Die Benutzung von Proxy-Klassen (auch Lazy Materialization genannt). Proxy-Klassen greifen erst dann auf die Datenbank zu, wenn die Daten tatsächlich benötigt werden. So kann beispielsweise verhindert werden, dass sämtliche Daten einer Tabelle in Java-Objekte umgewandelt werden, obwohl später nur ein einziges Objekt (z.B. an Position x in der Collection) benutzt wird.

OJB

Kommen wir zum speziellen O/R-Mapping Tool OJB. Die beiden herausragenden Merkmale von OJB sind das ODMG 3.0-konforme API sowie die erwartete, vollständige JDO (Java Data Objects)-Implementierung. Das ODMG-Konsortium (Object Data Management Group), mit seinen Mitgliedern aus Firmen wie POET Software, Sun oder eXcelon, legte weltweit den akzeptierten Standard für die Speicherung von Objekten in Datenbanken fest. Dieser Standard wird als Grundlage für JDO angesehen und so verwundert es nicht, dass die ODMG die Basis für die JDO Spezifikation geliefert hat. Für den Entwickler bedeutet dies: Stabilität und Zukunftssicherheit für OJB.

Zu Grunde dieser APIs von OJB liegt das PersistenceBroker-API, welches von den ODMG- und JDO-APIs benutz wird und die Aktionen wie SELECT, INSERT oder UPDATE auf der Datenbank ausführt. Über dieses API können Objekte mit simplen Methoden-Aufrufen wie etwa store() persistent gemacht werden, genauso einfach können Objekte über Queries oder Referenzen auf den Fremdschlüssel wieder erstellt werden.
Was benötigen Sie für OJB? Eine Datenbank (in unserem Beispiel MySQL), die OJB JAR-Files im Classpath und Beispiel-Klassen zum Testen des Persistenz-Mechanismus sowie eine Test-Applikation. Dem voraus geht die einmalige Einrichtung der Datenbank für OJB (OJB speichert Meta-Informationen in eigenen Tabellen in der Datenbank ab), sowie die Erstellung der benutzerspezifischen Datenbanktablen für Ihre eigenen Objekte und die Konfiguration des O/R-Mappings mittels der Datei repository_user.xml.

Für unser Beispiel zu OJB gehen wir davon aus, dass Sie OJB auf Ihrem System mit einer MySQL-Datenbank eingerichtet haben. Anstatt hier die Dokumentation zu wiederholen, möchte ich auf die Website von OJB verweisen: jakarta.apache.org/ojb/quickstart.html. OJB ist dann für unsere Demo-Applikation bereit, wenn die Datenbank ojb mit Ihren 9 internen Tabellen angelegt wurde.

Die Demo-Applikation

Unsere Beispiel-Applikation besteht aus zwei Klassen, FotoCollection und FotoImage. FotoCollection enthält eine Collection von FotoImage-Objekten, welche somit zu einem Fotoalbum gehören. Abpictureung 1 veranschaulicht diese beiden Klassen in UML:

Abb. 1: Darstellung der Beispiel-Klassen in UML

Sie sehen, dass beide Klassen normale JavaBeans sind, welche über Properties und die dazugehörigen Getter– und Setter-Methoden verfügen. Beachten Sie, dass jede der beiden Klassen auch über ein Property id verfügt, welches später als Primärschlüssel in der Datenbank verwendet wird. Dies ist eine Voraussetzung, um mit OJB arbeiten zu können. Da eine FotoCollection viele FotoImages haben kann, wurde in der Klasse FotoImage eine Fremdschlüsselbeziehung zu FotoCollection eingeführt: collectionId referenziert den Primärschlüssel aus FotoCollection, um deutlich zu machen, zu welcher Bildersammlung dieses Bild gehört.

Nachdem Sie die Java-Klassen identifiziert haben, welche gespeichert werden sollen, müssen Sie nun die Datenbanktablen für diese Klassen anbiete. Hier bietet OJB auch einige Tools an, um JavaBeans in den entsprechenden SQL-Code zu verwandeln. In unserem Beispiel bietet es sich jedoch an, die Tabellen selbst zu erstellen. Mit PhpMyAdmin oder direkt über SQL sind die beiden Tabellen (übrigens direkt in der Datenbank ojb) schnell erstellt:

CREATE TABLE FOTOCOLLECTION (
ID    		INT PRIMARY KEY,
DESCRIPTION		VARCHAR(200)
);
CREATE TABLE FOTOIMAGE (
ID    		INT PRIMARY KEY,
COLLECTIONID	INT,
PATHTOIMAGE		VARCHAR(200),
DESCRIPTION		VARCHAR(200)
);

Spannend wird es nun bei der Konfiguration des O/R-Mappings. Zunächst wird die Datei repository.xml auf unsere Datenbank (MySQL) konfiguriert:

Stellen Sie auch sicher, dass der entsprechende Treiber für MySQL im Classpath vorhanden ist. Sie können an der Datei repository.xml bereits erkennen, dass noch andere Dateien per Entities in die XML-Datei miteinbezogen werden. Darunter ist auch die Datei repository_user.xml, in welcher alle User-spezifischen Mappings definiert werden (Listing 1).

Listing 1

Das XML-Element class-descriptor gibt jeweils das Mapping für eine Java-Klasse an. Darin befinden sich die field-descriptors, welche unsere JavaBean-Properties in die Tabellen der Datenbank übertragen. Mit dem Attribut name wird das Property der JavaBean referenziert und mit dem Attribut column wird die entsprechende Datenbanktable angegeben. Des Weiteren muss der JDBC-Type angegeben werden. Hier muss man angeben, um welche Art von Daten es sich bei dem jeweiligen Property handelt. Auf der Website des Projektes kann eine vollständige Tabelle betrachtet werden, die wichtigsten Konversionen sind in Tabelle 1 dargestellt:

JDBC-Type Java-Type
VARCHAR string
BIT boolean
INTEGER int
DATE java.sql.Date

Da die Klasse FotoCollection eine Collection von FotoImages enthält, muss der Eintrag für dieses Feld in der repository_user.xml etwas besonderes sein: Der Eintrag ist kein field-descriptor, sondern ein collection-descriptor (Listing 2).

Listing 2

Das Property images in FotoCollection soll eine Collection von FotoImages zurückgeben. Als Attribut element-class-ref wird somit zunächst einmal die referenzierte Klasse angegeben, welche später in der Collection gespeichert werden wird. auto-retrieve stellt sicher, dass alle Bilder automatisch mit der Klasse FotoCollection mitgeladen werden. Ebenso werden die Elemente der Collection automatisch mit der Datenbank geupdatet, wenn dort Veränderungen vorgenommen worden sind und die FotoCollection wieder gespeichert wird (auto-update). Sollten Bilder der Collection entfernt werden, so werden diese beim Update der FotoColleciton in der Datenbank gelöscht (autot-delete). Durch die Attribute orderby und sort geben wir eine Sortierung der Collection nach dem id-Feld an.
Ein weiteres Element ist in unserem Collection Descriptor enthalten:

Es ist eine Referenz auf das Mapping der Klasse FotoImage, und zwar deren zweites Feld:

Da bei einem Update auch die FotoImages geupdatet werden müssen und die Fremdschlüsselbeziehung zwischen FotoImage und FotoCollection überprüft werden muss, wird hier das Feld aus FotoImage referenziert. Dieses Feld aus FotoImage referenziert die ID der entsprechenden FotoCollection, welcher dieses Bild angehört. Alle Klarheiten beseitigt?

Da nun die Datenbank eingerichtet ist, alle OJB JAR-Files und der MySQL-Treiber im Classpath ist und wir das O/R-Mapping definiert haben, können wir nun in einer kleinen Test-Applikation das Mapping testen (Listing 3). Wir erstellen zwei Anwendungsfälle, die der Reihe nach abgearbeitet werden. Zunächst wird innerhalb der Methode createData() eine FotoCollection erstellt, Bilder werden hinzugefügt und dies wird gespeichert. Die Methode retrieveData() lädt die gespeicherte FotoCollection, gibt die Anzahl der Bilder aus und fügt ein weiteres Bild hinzu.

Listing 3

package persistence;

import org.apache.log4j.Logger;
import org.apache.ojb.broker.*;
import org.apache.ojb.broker.query.Criteria;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.query.QueryByCriteria;

import bo.Address;
import bo.FotoCollection;
import bo.FotoImage;
import bo.Person;

import java.util.*;

/**
*
* @author sven
*/
public class TestOjb
{

private PersistenceBroker broker;

/**
* Constructor for TestOjb.
*/
public TestOjb()
{
broker =
PersistenceBrokerFactory.createPersistenceBroker(
"repository.xml",
"root",
"");

createData();
retrieveData();
}

public static void main(String[] args)
{
new TestOjb();
}

private void createData()
{

FotoCollection album = new FotoCollection();
album.setDescription("meine privaten bilder");

Collection images = new ArrayList();
FotoImage image1 = new FotoImage();
image1.setDescription("im kaunertal");
image1.setPathToImage("/usr/local/bild.gif");
images.add(image1);

FotoImage image2 = new FotoImage();
image2.setDescription("im stubaital");
image2.setPathToImage("/usr/local/bild2.gif");
images.add(image2);

album.setImages(images);

//store it!
broker.beginTransaction();

// make the new object persistent
broker.store(album);
//broker.store(address); // not needed, auto-update turned on!
broker.commitTransaction();
}


private void retrieveData()
{

//load the data, make changes, check database afterwards
Criteria crit = new Criteria();
crit.addEqualTo("id", "1");
Query query = new QueryByCriteria(FotoCollection.class, crit);

FotoCollection loadAlbum = (FotoCollection) broker.getObjectByQuery(query);

System.out.println("FotoCollection Description: " + loadAlbum.getDescription());
System.out.println("Images No.: " + loadAlbum.getImages().size());

FotoImage image3 = new FotoImage();
image3.setDescription("noch ein bild");
image3.setPathToImage("usr/local/bild3.gif");

loadAlbum.getImages().add(image3);

//store it again!
broker.beginTransaction();

// 2. make the new object persistent
broker.store(loadAlbum);
//broker.store(address); // not needed, auto-update turned on!
broker.commitTransaction();

}

}

Um Objekte über das PersistenceBroker-API zu speichern, sind nun nur wenige Zeilen Code notwendig:

//store it!
broker.beginTransaction();
// make the new object persistent
broker.store(album);
//broker.store(address); // not needed, auto-update turned on!
broker.commitTransaction();

Die Methode des PersistenceBrokers store() nimmt uns sämtliche Arbeit ab. Automatisch werden durch das Attribut auto-update in der repository_user.xml auch alle FotoImages gespeichert.
Das Wiederherstellen der FotoCollection ist etwas schwieriger. Zunächst wird eine QueryByCriteria erstellt, um die Datenbank nach Objekten der Klasse FotoCollection zu durchsuchen.

Criteria crit = new Criteria();
crit.addEqualTo("id", "1");
Query query = new QueryByCriteria(FotoCollection.class, crit);

In SQL ausgedrückt würde crit.addEqualTo(„id“, „1“); folgendem Ausdruck gleichen:

WHERE id = 1

Über die getObjectByQuery()-Methode wird zuletzt die komplette FotoCollection geladen. An diese Methode wird die soeben erstellte Query übergeben.

Anhand eines ersten Beispiels haben Sie nun gesehen, wie schnell man mit OJB arbeiten kann. Die Erstellung der Datei repository_user.xml für das O/R-Mapping ist der Knackpunkt für die erfolgreiche Arbeit mit OJB. Jedoch werden Sie nach einigen kleinen Beispielen schnell feststellen, dass diese Datei sehr einfach und logisch aufgebaut ist. Oft können Bestandteile kopiert werden und: Sobald diese Datei einmal erstellt wurde, können Sie sämtliche Operationen auf dieses Klasse über die vorhandenen APIs (PersistenceBroker-API, ODMG-compliant-API, in Zukunft JDO-API) ausführen.

Dieses Beispiel zeigte nur einen sehr kleinen Teil des mittlerweile mächtigen Funktionsumfanges von OJB. Selbstverständlich sind auch die Abpictureung von m:n-Beziehungen oder die Benutzung von Polymorphismus kein Problem. Ebenfalls kann OJB in einem Multiserver-Szenario eingesetzt werden (Verteilung auf mehrere Rechner, Einrichtung eines Loadbalancings), wodurch OJB höchstskalierbar wird.

OJB muss sich als Open Source-Projekt nicht vor seiner kommerziellen Konkurrenz fürchten. Bereits jetzt lässt sich OJB absolut zuverlässig in kommerziellen Applikationen einsetzen. Gerade für Web-Applikationen (z.B. mit dem Struts Web Application Framework) lassen sich so schnell Web-Applikationen entwickeln. Nach der Übertragung der Anwendungsfälle auf die Business Delegates kann durch die Benutzung von OJB die Komplexität der Business Delegates stark verringert werden.

Probieren Sie es aus!

Thomas Mahler leitet das Jakarta OJB Projekt. Er arbeitet als Senior Consultant für objektorientierte Technologien bei der Itellium Systems & Services GmbH in Essen (www.itellium.com). Seine Arbeit konzentriert sich auf J2EE-basierte Softwarearchitekturen und Frameworks. Das Interview wurde am 17.01.03 per eMail geführt.

Java Magazin: Worin liegen Ihrer Meinung nach die Vorteile von OJB gegenüber anderen O/R-Mapping Tools?

Thomas Mahler: Das ist eine Frage, die ich oft beantworten muss, da es inzwischen ein breites Angebot an kommerziellen und freien O/R-Tools gibt. Ein wichtiger Punkt scheint mir die Unterstützung mehrerer APIs zu sein. Viele Tools bieten nur ein einziges “ und evtl. proprietäres “ API. OJB lässt hingegen dem Benutzer die freie Wahl. Zur Zeit unterstützt OJB die ODMG- und JDO-Standard-APIs, sowie ein einfaches Kernel-API. Auch benutzerdefinierte Aufsätze und Erweiterungen sind mit wenig Aufwand machbar.
Ein weiteres Plus, das sonst nur kommerzielle Produkt aus dem High-end Segment bieten, sind Skalierbarkeit und Integrationsmöglichkeiten in J2EE Applikations-Server. Auch die Integrierbarkeit mit den anderen Jakarta-Produkten wie Tomcat, Struts, Turbine, Torque, Log4J, DBCP, ANT, Maven etc. ist natürlich gegeben.
Wichtig für viele Benutzer ist, das OJB mit einer umfassenden Regressions-Testsuite ausgeliefert wird. So lässt sich vor Ort ermitteln, ob es Kompatibilitätsprobleme mit JDBC-Treibern und den Datenbanken gibt. Für das Entwicklerteam ist diese Testsuite das wichtigste QS-Instrument.
Als Jakarta-Projekt haben wir eine solide organisatorische Basis und eine breite Benutzer Community, die aktiv ins Geschehen eingreift. Das verschafft uns einen gesunden Mix aus Stabilität und Dynamik, der ohne die Apache Foundation so nicht denkbar wäre.

JM: Welche Erfahrungen haben Sie mit der Performance von OJB gegenüber selbst-implementierten Persistenz-Mechanismen gemacht?

Mahler: Die Performance von Datenbankanwendungen wird meiner Erfahrung nach durch JDBC-Treiber, Netzwerk- und Datenbanklatenzen bestimmt. Die Datenbankzugriffsschicht ist nur ein sekundärer Faktor. Dies wird auch durch Performancevergleiche von OJB mit anderen O/R-Tools belegt. Die Unterschiede bewegen sich meist im Rahmen der Messgenauigkeit.
Das entscheidende Argument gegen Individualentwicklungen sind die Wartungskosten. Ich kenne einige Teams, die aus Kostengründen ihre Eigenentwicklungen eingestellt haben und die jetzt auf OJB setzen. Hier bewährt sich auch unsere flexible Komponentenarchitektur. So haben z.B. OJB-Anwender bereits selbstentwickelte Stored Procedure basierte Persistenzschichten in OJB integriert.
In vielen Projekten herrscht anfangs Unsicherheit, ob der Einsatz eines O/R-Tools nicht unvertretbare Performanceeinbußen mit sich bringt. Wir liefern deshalb auch eine Performance-Testsuite mit aus, die es erlaubt die OJB-Performance gegen JDBC als Referenz zu messen. Diese Suite kann direkt in der Zielumgebung der Benutzer ausgeführt werden und kann damit eine wesentliche Entscheidungshilfe sein.

JM: Wann rechnen Sie mit einer JDO-Implementierung?

Mahler: Für unsere ungeduldigen User bieten wir schon heute ein OjbStore-Plugin an. Dieses Plugin klinkt sich in die JDO-Referenzimplementierung von Sun ein und benutzt OJB als Datenbankzugriffsschicht. Man kann also schon jetzt JDO-konforme OJB-Anwendungen schreiben.
Leider ist diese Lösung aus Performancegesichtspunkten noch nicht optimal. Mit einer vollständigen JDO-Implementierung ist aber noch in diesem Jahr zu rechnen.
Hier gilt wie überall in der Open Source Community: Jede Hilfe ist willkommen! Ich habe es schon mehrfach erlebt, das Dinge für die ich mehrere Wochen Aufwand kalkuliert hatte, innerhalb weniger Tage erledigt wurden, weil jemand mit Expertenwissen seine Hilfe angeboten hat.

Geschrieben von
Sven Haiges
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.