ITMAGAZINE Java-Objekte mit SQL verheiraten 24. November 2006 - iBatis ermöglicht die Abstraktion von SQL-Datenbanken, ohne dass man auf von Hand geschriebene SQL-Abfragen verzichten muss. Entwickelt man Applikationen auf modernen objektorientierten Plattformen wie Java, so stellt sich oft die Frage, wie man Objekte sauber persistiert. Häufig wird man auf den Komfort und die Leistungsfähigkeit, die relationale Datenbanken unter anderem in bezug auf Abfragen, Transaktionen und Backup bieten, nicht verzichten wollen. Nun existiert mit JDBC in Java ein standardisiertes Interface zum Zugriff auf relationale Datenbanken. Die «direkte» Verwendung von JDBC hat jedoch in der Praxis viele Nachteile: - Java-Code und SQL-Statements sind üblicherweise vermischt oder lassen sich nur schwer voneinander trennen. - Die Anwendung ist an eine bestimmte Datenbank gebunden. - Der Zugriff ist wenig elegant und unflexibel, weil die «Welt der Objekte» und die «Welt der Tabellen» konzeptionell sehr unterschiedlich sind. - Oft ist eine flexible Konfiguration erforderlich (Transaktionsmanager, Connection Settings, Datenbank), was mit direkter JDBC-Programmierung schwer zu erreichen ist Viel Abstraktions-Aufwand Meist wird daher bei modernen Anwendungen ein objektrelationales (O/R) Mapping-Tool eingesetzt, das die Verbindung zwischen Objekten, die persistiert werden sollen, und der Datenbank herstellt. Es gibt hier unzählige Frameworks, beispielsweise Hibernate, Castor, OJB und Toplink. Dennoch sind diese Frameworks nicht uneingeschränkt zu empfehlen. Hibernate & Co. erlauben, detaillierte Abbildungen von Objekthierarchien auf Tabellen in einer relationalen Datenbank zu definieren. Der Entwickler arbeitet dann nur mehr auf Objektebene und bekommt sogar eigene Abfragesprachen wie HQL zur Verfügung gestellt. Die konkreten SQL-Statements, die für den Zugriff benötigt werden, werden vom Framework je nach verwendeter Datenbank automatisch generiert. Weiter bieten diese Frameworks typischerweise vollen Roundtrip-Support an. So kann man die Mappings in Java-Annotationen deklarieren und dann die konkreten Mapping-XML-Dateien sowie die Datenbank generieren lassen. Diese Abstraktion ist in vielen Fällen sehr nützlich und leistungsfähig, birgt aber auch verschiedene Nachteile sowie Risiken: Die Verwendung von Frameworks wie Hibernate ist in der Praxis ziemlich komplex, die Einarbeitungszeit entsprechend hoch. Man muss die Mapping-Definitionen verstehen, die neue Abfragesprache, die Tools sowie die vielen Konfigurationsmöglichkeiten wie Caching. All dies kann sehr aufwendig sein, besonders dann, wenn Code-Generierung verwendet wird. Dies ist eigentlich fast immer anzuraten, weil sonst die Gefahr besteht, dass verschiedene Teile der Anwendung ausser Synchronität geraten. Frameworks wie Hibernate sind daher vor allem in grossen und komplexen Projekten angeraten, wenn auch ein entsprechender Experte des jeweiligen Frameworks zur Verfügung steht. Vorteile ohne Nachteile iBatis hingegen geht einen anderen Weg. Bruce Tate beschreibt es in seinem Buch «Spring Developers Notebook» so: «iBatis ist ein JDBC unterstützendes Framework, das viele Vorteile von O/R-Mappern bringt, aber nicht deren Risiken mitbringt.» iBatis ist eigentlich auch kein O/R Mapping Framework, vielmehr werden anstelle von Tabellen SQL-Statements auf Objekte gemappt. Das heisst, SQL ist ein zentraler Bestandteil von iBatis-Anwendungen, allerdings werden diese sauber vom Java-Code über XML-Maps getrennt. Dies hat klare Vor- und Nachteile: Einerseits hat man die volle Leistungsfähigkeit von handoptimiertem SQL zur Verfügung, was bei Hibernate nur «durch die Hintertür» möglich ist. Gleichzeitig ist damit natürlich die Bindung an eine konkrete Datenbank auch höher, was aber in vielen Anwendungsfällen unproblematisch ist. Das Unterstützen einer weiteren Datenbank bedeutet, dass neue Mappings erstellt werden müssen, wozu allerdings kein Eingriff in den Java-Code notwendig ist. Auch Stored Procedures werden besser unterstützt. Bei einem niedrigeren Grad an Abstraktion macht dieser Zugang das Mapping natürlich wesentlich einfacher. Karten für SQL iBatis benötigt eine Konfigurationsdatei (sqlMap-config.xml), in der die wesentlichen Optionen für den Datenbankzugriff deklariert werden. Dazu gehört die Konfiguration der verwendeten Datenquelle (z.B. ein Connection Pool), eines eventuell verwendeten Transaktionsmanagers sowie die Definition der eigentlichen SQL-Map. Der Kern einer Applikation mit iBatis ist nun die Beschreibung der Mappings (siehe auch Abbildung 1). Im wesentlichen wird für jedes zu persistierende Objekt eine SQL-Map erstellt. In dieser Map können für dieses Objekt verschiedene Operationen definiert werden wie «Insert», «Update» oder «Get» Jede dieser Operationen wird durch ein beliebiges SQL-Statement beschrieben. Das heisst, das Objekt wird an verschiedene SQL-Statements gebunden, und diese entscheiden dann, welche Tabellen für die Abarbeitung verwendet werden. Dies lässt sich am besten mit einem Beispiel illustrieren: Soll das Objekt «Person» (mit nachname, vorname, email) in die Datenbanktabelle «PERSON» gespeichert werden, so ist ein Mapping zu definieren, das die Übergabeparameter aus dem Objekt übernimmt und ein INSERTStatement formuliert, das an die Tabelle angepasst ist. Es könnte etwa so aussehen: INSERT INTO PERSON ( FIRST_NAME, LAST_NAME, EMAIL ) VALUES ( #vorname#, #nachname#, #email# ) Diese Anweisung wird von iBatis als Prepared Statement abgelegt und kann aus dem Java-Code dann einfach aufgerufen werden: Pers p = new Pers (“Alexander“, “Schatten“, “[email protected]“); sqlMap.insert(“insertPers“, p); Natürlich muss das SQL-Map-Objekt vom iBatis Typ SqlMapClient sein und vorher entsprechend initialisiert werden. Weiter kann man im Mapping-Dokument auch SQL-Fragmente wiederverwenden, wenn diese in verschiedenen Statements benötigt werden. In der Verwendung von Übergabe- und Rückgabe-Parameter ist man flexibel: Es können Java-Primitive verwendet werden, Collections oder eigene Typ-Konverter. So kann man boolsche Werte beispielsweise auf Strings wie «Ja» und «Nein» abbilden. Abbildung 1: iBatis-Architektur Direkter Zugriff Neben den genannten Funktionen bietet iBatis weiter eine DAO-Bibliothek an. Diese ist optional und muss nicht verwendet werden. Jedoch empfiehlt es sich in jedem Fall, das DAO-Enterprise-Pattern für eigene Anwendungen einzusetzen – egal, ob man nun die iBatis-Bibliothek verwendet oder es selber implementiert. Der Grundgedanke des DAO-Pattern ist in Abbildung 2 illustriert. Wesentlich ist hier, dass Zugriff auf Persistenz-Mechanismen nur von ausgewählten Klassen, den DAOs, erfolgt und nicht quer über die Anwendung verteilt mit Persistenz-Klassen wie den SQL-Maps interagiert wird. Praktisch bedeutet dies folgendes: - Man definiert ein Interface, in dem in neutraler Weise Zugriffsmethoden definiert sind; diese sind unabhängig vom verwendeten Persistenz-Mechanismus. - Man implementiert konkrete DAOs, beispielsweise für iBatis, für XML oder für Hibernate, je nachdem, was benötigt wird. - In den «Geschäftsobjekten» arbeitet man ausschliesslich mit Variablen vom Interface-Typ und holt sich die konkrete DAO-Instanz von einem Container wie Spring. - Ein Transfer-Objekt (ein normales Java-Bean) wird verwendet, um Daten auszutauschen. Der grosse Vorteil dieses Ansatzes ist es, dass jederzeit der Persistenz-Mechanismus ausgetauscht werden kann – von iBatis auf XML oder auf Hibernate –, ohne dass die Business-Logik verändert werden muss: Man muss «nur» neue DAOs implementieren und die Konfiguration des Containers ändern. Abbildung 2: Aufbau des DAO-Patterns Einsteigen und losfahren Der Einstieg in iBatis sollte nicht allzu schwerfallen. Einerseits, weil das Konzept leicht verständlich ist, andererseits aber auch, weil die Dokumentation sehr gut ist. Es existieren ein kurzes Tutorial, das auf wenigen Seiten die Prinzipien mit Code-Beispielen erklärt, ein umfangreicheres Handbuch sowie eine Beschreibung der Data-Access-Object-Bibliothek. Parallel dazu gibt es umfangreiche Sekundärliteratur wie «iBatis in Action» (ISBN 1-93239-482-6) oder «Spring, a Developer’s Notebook» (ISBN 0-596-00910-0) von Bruce Tate. iBatis wird weiter (ebenso wie Hibernate, Castor und OJB) vom Framework Spring unterstützt, das heisst, man kann sehr einfach die bekannten Spring-Mechanismen zur Konfiguration (Dependency Injection) sowie deklarative Transaktionen und Templates nutzen. Neben Java existieren auch Portierungen für .Net und Ruby. Spezifische Datenbank-Klassen sind nicht erforderlich, da ja beliebige JDBC-Treiber sowie SQL-Statements verwendet werden können. Daher sollte iBatis mit allen bekannten relationalen Datenbanken funktionieren. Wann iBatis einsetzen? iBatis ist empfehlenswert, wenn: · volle O/R-Funktionalität nicht unbedingt erforderlich ist und man ein sehr schnell zu erlernendes und dennoch leistungsfähiges Framework verwenden möchte. · bestehende Datenbanken integriert werden sollen. · Datenbank- und Anwendungsentwicklung getrennt sind. · volle Leistungsfähigkeit von optimierten SQL-Statements genutzt werden soll. · das Datenmodell eher einfach ist. · kein gut geschulter Hibernate-Experte vor Ort ist. iBatis ist hingegen weniger geeignet, wenn: · das Datenmodell komplex ist (Vererbungen etc.) · von SQL abstrahiert werden soll (einfache Unterstützung verschiedener Datenbanken) · Erstellen von Datenbank-Metadaten und Mappings automatisiert werden soll (Codegenerierung) Der Autor Alexander Schatten ([email protected]) ist Assistent am Institut für Softwaretechnik und Interaktive Systeme der Technischen Universität Wien. Copyright by Swiss IT Media 2017