Java-basiertes Object-Mapping für XML, RDBMS und LDAP

Castor-Transport

Sebastian Eschweiler

Seit jeher steht Java als plattformunabhängige Programmiersprache zwischen den Welten der verschiedenen Computersysteme. Aber gerade diese Tatsache hat dazu beigetragen, dass eine Reihe von übergreifenden Standards geschaffen wurden, um sich mehr und mehr von der Plattformbindung zu lösen. Wer in seinen Java-Projekten auf XML, SQL oder LDAP setzt, findet in Castor ein Data-Bind ing-Framework, das alle Anforderungen dieser Bereiche vereint und ein optimales Zusammenspiel mit der Programmiersprache Java garantiert. Der folgende Artikel bietet eine einfache und verständliche Einführung in die Anwendung des Frameworks Castor.

Wer sich zur Aufgabe stellt, alle Funktionen des Castor Data-Binding-Frameworks in einem Satz zu nennen, wird daran vermutlich schnell scheitern. Bereits wer die Aufzählung der Überschrift zu diesem Artikel beachtet und Java-Objekte, XML-Dokumente, SQL-Tabellen und LDAP-Verzeichnisse kennt, wird schnell die große Funktionsvielfalt von Castor erahnen können. Zunächst lassen sich zwei grundlegende Bereiche des Frameworks unterscheiden:

  • Java-XML-Bindung (Castor XML)
  • Java-SQL/LDAP Persistence (Castor JDO, Castor DAX, Castor DSML)

Beide Punkte haben natürlich das Ziel der Abpictureung von Datensätzen auf Java-Objekte. Hierzu erhalten Sie mit Castor vielfältige Möglichkeiten, denn als Datenquellen können XML-Dokumente, SQL-Tabellen und LDAP-Verzeichnisse dienen. Selbstverständlich lassen sich diese Anwendungsfelder auch sehr einfach verbinden. Im nachfolgenden Artikel werden wir uns ebenfalls an diese Aufteilung halten und im ersten Teil auf die XML-Funktionen im Framework Castor eingehen. Der zweite Teil geht anschließend detailliert auf die Verwendung von Castor mit RDBMS ein. Die angesprochenen Funktionen zum Zugriff auf LDAP-Verzeichnisse werden in einem gesonderten Artikel in den folgenden Ausgaben des Java-Magazins betrachtet.

Castor XML

Castor XML beschreibt zunächst allgemein den Teil von Castor, der sich mit der XML-Verarbeitung und der Abpictureung von XML-Daten auf Java-Objekte beschäftigt. Dieser Teil des Frameworks lässt sich also als XML-Data-Binding-Tool beschreiben. Betrachten wir zunächst das Marshalling-Framework, welches Bestandteil von Castor XML ist. Hiermit erhalten Sie die Möglichkeit, Daten aus XML-Dokumenten in Java-Objekte bzw. Java- Objekte in XML-Dokumente zu überführen.

Bevor Castor jedoch einsatzfähig ist, müssen wir zunächst die Installation des Frameworks ansprechen. Die entsprechenden Schritte sind denkbar einfach, da Castor im Wesentlichen aus einer einzigen Jar-Datei besteht, die lediglich in den Classpath eingebunden werden muss, damit die Castor-Funktionen zur Verfügung stehen. Das entsprechende Jar-Archiv ist auf der Webseite des Castor-Projekts [1] www.castor.org zu finden.

Das Marshalling-Framework

Das Marshalling-Framework kommt für die Umwandlung von Java-Objekten in XML und den umgekehrten Weg zum Einsatz. Hierdurch erhalten Sie einen Ansatz der über die Fähigkeiten von SAX und DOM hinausgeht und die betreffenden Daten nicht strukturell, sondern lediglich inhaltlich verwaltet. Zum Erzeugen der XML-Dokumente brauchen in diesem Fall keine Strukturen von Hand erzeugt werden – dies bleibt dem Castor-XML-Framework überlassen. Am besten lässt sich dieses Prinzip an einem einfachen Beispiel erläutern, welches zunächst die Umwandlung eines Java-Objekts in ein entsprechendes XML-Dokument vornimmt.

Zunächst wird eine Java-Klasse benötigt, die ein Objekt in Form einer Klasse enthält, welches die zu speichernden Daten aufnimmt. Listing 1 zeigt hierzu eine Klasse mit dem Namen Film, welche drei Informationen über Filme enthält. Hierzu zählen der Filmtitel, das Erscheinungsjahr und die Filmlänge.

Listing 1

public class Film implements java.io.Serializable
{
private String title = null;
private Integer laenge = new Integer(0);
private Integer jahr = new Integer(1900);


public Film ()
{
super();
}

public Film (String title)
{
this.title = title;
}

public void setTitle (String title)
{
this.title = title;
}

public String getTitle ()
{
return title;
}

public void setLaenge (Integer laenge)
{
this.laenge = laenge;
}

public Integer getLaenge()
{
return laenge;
}

public void setJahr(Integer jahr)
{
this.jahr = jahr;
}

public Integer getJahr()
{
return jahr;
}
}

Wichtig ist hierbei die Implementierung eines public-Konstruktors und die Verwendung von getter- und setter-Methoden. Diese Vorgehensweise erinnert stark an die Programmierung von Java-Beans, die der gleichen Konvention entspricht. Eine Instanz der Klasse kann nun dem Marshaller übergeben werden, der sich anschließend um die Überführung der Datenstruktur in eine XML-Datei kümmert. Listing 2 verdeutlicht diese Vorgehensweise anhand eines Datensatzes vom Typ Film.

Listing 2

Film film = (Film)Unmarshaller.unmarshal(Film.class, reader);

Der letzte Abschnitt hat Ihnen nun gezeigt, wie einfach die Übertragung von Daten zwischen Java-Objekten und XML-Dokumenten ist. Wir greifen mit der Marshaller- und Unmarshaller-Funktion bei den vorherigen Beispielen auf die Standardeinstellung von Castor zurück. Hierbei nehmen wir keinen Einfluss auf den Mapping-Vorgang und verlassen uns auf die Vorgaben von Castor. Wie Sie sich vielleicht vorstellen können, bietet das Castor-Framework auch in diesem Punkt Möglichkeit zur Anpassung. Alle gewünschten Einstellungen zur Transformation, die vom Standardverhalten Castors abweichen, werden in einer Mapping-Datei hinterlegt.
Die Mapping-Datei muss dem Marshaller durch die Methode setMapping(Mapping mapping) mitgeteilt werden. Hierzu legen Sie zunächst ein Objekt vom Typ Mapping an (Listing 3).

Listing 3

Unmarshaller unmar = new Unmarshaller(mapping);

Wie Sie bereits im Beispielprogramm gesehen haben, handelt es sich bei der Mapping-Datei um ein XML-Dokument. Der Aufbau ist sehr einfach gehalten und klar gegliedert. Das Root-Element der Mapping-Beschreibung ist grundsätzlich das Element >mapping/>. Innerhalb dieses Root-Elements können verschiedene Angaben gemacht werden, dazu zählen:

  • eine Beschreibung mittels des Elements
  • die Einbindung weiterer Mapping-Dateien durch das Element
  • Klassenbeschreibungen durch

Hieraus ergibt sich folgende Struktur der Mapping-Datei:

Das Element erfordert nun besondere Aufmerksamkeit, da in diesem Abschnitt die grundlegenden Einstellungen zur Transformation der Java-Objekte hinterlegt werden. Zunächst wird durch das Attribut name der Name der Klasse festgelegt. (Für weitere Möglichkeiten der Attribute werfen Sie bitte einen Blick in das Castor-Handbuch.)
Innerhalb des Elements können nun alle Informationen zum Mapping einer bestimmten Klasse untergebracht werden. Zunächst ist über das Element festzulegen, wie das XML-Element benannt werden soll, das die aktuelle Klasse repräsentiert. Die Eigenschaften einer Klasse werden anschließend über Elemente in die Mapping-Informationen aufgenommen. Um nun beispielsweise die Eigenschaft Titel einer Klasse aufzunehmen, kann die Mapping-Datei wie folgt angepasst werden:


...

...

Dieses Beispiel behandelt die Eigenschaft Titel der Klasse Film. Über das Attribut name wird zunächst der Name der Klasseneigenschaft übergeben und das Attribut direct informiert Castor schließlich noch darüber, dass es sich bei Titel um ein public-Objekt handelt, welches direkt benutzt werden kann. Castor wird somit keine getter- und setter-Methoden anlegen. Der Typ des Objekts wird durch das Attribut type festgelegt. Hierbei kommt der vollständige Java-Klassenname zum Einsatz. Für die gebräuchlichsten Java-Klassen stehen außerdem Abkürzungen zur Verfügung, die anstelle des kompletten Klassennamens eingesetzt werden können. Hierzu finden Sie in der Castor-Dokumentation und auf der Castor-Homepage weitere Informationen.
Innerhalb des field-Elements legen Sie weiterhin über >bind-xml/> fest, wie das beschriebene Feld der Klasse in die XML-Datei übertragen werden soll. Das Attribut name legt den Namen innerhalb des XML-Dokuments fest und node definiert, ob die Darstellung als Attribut oder Child-Element erfolgen soll. Dazu können die Werte attribute und element übergeben werden.

public void Marshal()
{
try
{
FileWriter writer = new FileWriter(new File(filme.xml));
Film film = new Film();
film.setTitle(Das schwarze Schaaf);
film.setJahr(new Integer(1960));
film.setLaenge(new Integer(90));
Marshaller.marshal(film, writer);
} catch (Exception e) {
e.printStackTrace();
}
}

Dieser Aufruf analysiert zunächst die durch den Parameter -i übergebene Schema-Datei und generiert anschließend aus diesen Informationen die benötigten Klassen. Alle Klassen befinden sich anschließend im Package de.foo. Natürlich können die neu generierten Klassen mittels javac problemlos kompiliert werden.

Wenn man die Funktionsweise des Source Code-Generators betrachtet, stellt sich natürlich die Frage, in welcher Weise die im Schema enthaltenen Funktionen ausgewertet werden, um die benötigten Informationen für eine Java-Klasse zu erhalten. Castor stellt Ihnen zwei unterschiedliche Methoden zur Klassengenerierung zur Verfügung. Zum einen kann der Source-Generator in Verbindung mit der so genannten element‘-Methode verwendet werden und zum anderen kann die type‘-Methode zum Einsatz kommen. Zunächst betrachten wir die element‘-Methode. Jeder top-level complexType des Schemas wird bei dieser Methode in eine abstrakte Klasse transformiert. Es werden keine Klassen für simpleType-Einträge erstellt. Diese Vorgehensweise lässt sich am besten an einem einfachen Beispiel verdeutlichen. Zunächst betrachten wir im folgenden Listing eine Schema-Datei:

public void Marshal()
{
try
{
FileWriter writer = new FileWriter(new File(filme.xml));
Film film = new Film();
film.setTitle(Das schwarze Schaaf);
film.setJahr(new Integer(1960));
film.setLaenge(new Integer(90));
Marshaller.marshal(film, writer);
} catch (Exception e) {
e.printStackTrace();
}
}

Das Schema legt zunächst einen complexType fest, der als Parameter den Namen der zu generierenden Klasse enthält. Innerhalb des complexType nimmt das Element die Elemente auf, die später in Eigenschaften (Variablen) der Klasse überführt werden können. Hierbei werden jeweils die Parameter name und type übergeben, die Informationen zum Typ und zum Namen der Klasseneigenschaft enthalten.

Die Klasse, die aus diesem Schema nach der Generierung mit der element‘-Methode entstehen würde, ist in Listing 4 abgedruckt.

Listing 4

public void Marshal()
{
try
{
FileWriter writer = new FileWriter(new File(filme.xml));
Film film = new Film();
film.setTitle(Das schwarze Schaaf);
film.setJahr(new Integer(1960));
film.setLaenge(new Integer(90));
Marshaller.marshal(film, writer);
} catch (Exception e) {
e.printStackTrace();
}
}

Wie eingangs besprochen, gehen wir nun zum zweiten Teil dieses Beitrags über und sehen uns – nach der Betrachtung der Castor XML-Funktionen – die Datenbankunterstützung mittels Java Data Objects an. Zunächst gibt es hierbei zwei unterschiedliche Anwendungsfälle zu unterscheiden: eine Datenbankverbindung kann in einer gewöhnlichen Client-Applikation vorkommen oder in einem J2EE Server-Umfeld. Im ersten Anwendungsfall muss eine Verbindung zur Datenbank mit Hilfe von JDBC von Hand aufgebaut werden. J2EE-Applikationen haben den Vorteil über JNDI Datenbankkonfiguration erfragen zu können. Somit brauchen hier keine Verbindungsparameter von Hand eingegeben werden. In Verbindung mit der Anwendung von Datenbankapplikationen bietet die J2EE-Plattform natürlich noch zahlreiche weitere Vorteile, auf die wir an dieser Stelle aber nicht weiter eingehen wollen. Vielmehr soll die weitere Betrachtung auf die Fähigkeiten des Castor-Frameworks in Verbindung mit den oben beschriebenen Anwendungsfällen gelenkt werden.
Java Data Objects übernehmen nun das Mapping von Java-Objekten zu SQL-Tabellen. Die Funktionsweise besitzt zudem viele Parallelen zum zuvor besprochenen XML-Mapping. Sie brauchen sich nicht um die Struktur der Java-Objekte zu kümmern und die Eigenschaften der Klasse einzeln von Hand auf die Datenbanktable abzupictureen. Diese Aufgabe übernimmt die JDO-Funktion von Castor automatisch. Hierbei ist die persistente Speicherung der Objekte zentrale Aufgabe.

public void Marshal()
{
try
{
FileWriter writer = new FileWriter(new File(filme.xml));
Film film = new Film();
film.setTitle(Das schwarze Schaaf);
film.setJahr(new Integer(1960));
film.setLaenge(new Integer(90));
Marshaller.marshal(film, writer);
} catch (Exception e) {
e.printStackTrace();
}
}

Nach der allgemeinen Einstellung der verwendeten Datenbank müssen nun die Treiber und die Verbindungsparamter festgelegt werden. Dies geschieht innerhalb des Elementes . Als Parameter werden bei diesem Element der Klassenname des Treibers durch class-name und die zugehörige URL durch url übergeben. Weiterhin müssen für den Zugang durch das Element >param> zwei zusätzliche Parameter übergeben werden. Dies sind zum einen der gültige Benutzername und zum anderen das zugehörige Passwort. Beachten Sie, dass dieses Vorgehen nur auf eine Oracle-Datenbank zutrifft. Wir sehen uns im Folgenden deshalb ein weiteres Beispiel für die Einstellungen zu einer PostgreSQL-Datenbank an. Schließlich befindet sich noch der Verweis auf die eigentlichen Mapping-Dateien innerhalb des XML-Dokuments. Eingeleitet durch das Element mapping übergibt der Parameter href die hierzu benötigte URL.

OQL

Um nun eine Anfrage an die zuvor konfigurierte Datenbank zu stellen, ist ein SQL-Statement sicherlich nicht das Mittel der Wahl, da das Objekt-Mapping gerade den Sinn verfolgt, sich von der Darstellung der Daten als Tabelle zu lösen. Vielmehr ist an dieser Stelle eine Abfragesprache gefordert, die mit Java-Objekten umgehen kann und die Fähigkeit besitzt, auf dieser Basis Datenbankabfragen zu generieren. Wie Sie sich sicherlich denken können, existiert auch für diesen Bereich innerhalb des Castor-Frameworks eine Lösung: OQL.

Bei OQL handelt es sich um eine Objekt-basierte Abfragesprache, die sich sehr eng an die Syntax von SQL anlehnt und daher für Umsteiger optimal geeignet ist. Wer zuvor keine SQL-Statements kennen gelernt hat, wird aber ebenfalls keine Probleme besitzen den Grundgedanken dieser Sprache zu verstehen.

Grundlegende Klassen für die Arbeit mit OQL in Castor sind OQLQuery und QueryResult. Ein OQLQuery-Objekt ist sehr einfach über ein zuvor angelegtes Database-Objekt zu initialisieren:

public void Marshal()
{
try
{
FileWriter writer = new FileWriter(new File(filme.xml));
Film film = new Film();
film.setTitle(Das schwarze Schaaf);
film.setJahr(new Integer(1960));
film.setLaenge(new Integer(90));
Marshaller.marshal(film, writer);
} catch (Exception e) {
e.printStackTrace();
}
}

Dieser Artikel hat einen ersten Einblick in das Framework Castor gegeben. Sie werden sicherlich festgestellt haben, dass die Funktionen, die Castor bietet, sehr vielfältig sind und für zahlreiche Anwendungsgebiete Lösungen bereit halten. Auf der Basis von Java ebnet Castor den einheitlichen Weg zu verschiedenen Standards der Datenspeicherung. Das zentrale Anwendungsgebiet des Object-Mappings wird durch eine Reihe von Zusatzfunktionen abgerundet, die Castor einzigartig erscheinen lassen. Gerade im Bereich der objektorientierten Programmierung ist es von zentraler Bedeutung bei der Verwaltung von großen Datenmengen nicht die Objektorientierung zu verlassen. Eine einheitliche Struktur wird wesentlich zu der Effektivität einer Anwendung beitragen. Castor gibt Ihnen alle Mittel mit auf den Weg, die Sie für die Lösung dieser Probleme brauchen.

Wie eingangs erwähnt, ist dieser Beitrag noch nicht auf Anwendungsfälle des Castor-Projekts in Verbindung mit LDAP-Verzeichnissen eingegangen. Zu diesem Thema finden Sie einen eigenen Artikel in einer der nächsten Ausgaben des Java Magazins.

Das Marshalling-Framework

[1] www.castor.org

Geschrieben von
Sebastian Eschweiler
Kommentare

Schreibe einen Kommentar

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