Jeder Java-Entwickler steht irgendwann vor dem Problem, Objekte und ihre Beziehungen persistent zu machen. Obwohl es objektorientierte Datenbanken schon seit langem gibt, dominieren in der Praxis ihre relationalen Verwandten. Die passen aber nicht recht zum Objektmodell, was im Fachjargon Impedance Mismatch heißt. Das Java-Paket Hibernate[1] (auf gut Deutsch: Winterschlaf halten) löst das Problem, indem es eine spezielle Softwarekomponente implementiert, die so genannte Persistenzschicht. Damit bildet es Objekte und ihre Attribute auf Tabellen mit Zeilen und Spalten ab. Diese Abbildung heißt objekt-relationales Mapping (ORM).
Objekte in Tabellen
Ein ORM-System selbst zu entwickeln ist nicht ganz einfach, man läuft Gefahr, einen Großteil der Entwicklungszeit in die Persistenzschicht zu investieren statt an der eigentlichen Aufgabenstellung zu arbeiten. Typischerweise macht das dann 30 Prozent oder mehr des gesamten Projektcodes aus.
Hibernate ist ein fertiges objekt-relationales Mapping-Framework für Java und erfreut sich als freie Software mittlerweile großer Beliebtheit. Das Projekt wird auf Sourceforge geführt und steht unter der Lesser GNU Public License. Die Dokumentation ist ausführlich und die Forenbenutzer leisten schnell qualifizierte Hilfe. Kommerziellen Support und Training bietet die Firma JBoss. Das folgenden Beispiel zeigt anhand einer einfachen Kundendatenbank, wie man Hibernate verwendet. Die Beziehungen zwischen den Tabellen sind in Abbildung 1 dargestellt, die zugehörigen Java-Klassen zeigt Abbildung 2.
Abbildung 1: Entity-Relationship-Diagramm der Beispieldatenbank: Zu einem Land »COUNTRY« gehören maximal »N« Adressen »ADDRESS« , jede Adresse kann aber nur »1« Land enthalten.
Abbildung 2: Dem Datenbankschema entsprechende Java-Klassen als UML-Diagramm. Die N:M-Relation zwischen »CUSTOMER« und »KEYWORD« aus Abbildung 1 wird von Hibernate auf ein einfaches Set in der Klasse »Customer« abgebildet.
Persistente Klassen bilden die Schnittstelle zwischen der Anwendung und der Datenbank. Diese Klassen, die der Java-Bean-Konvention[2] entsprechen müssen, werden von Hibernate auf die Tabellen der Datenbank abgebildet. Eine solche Bean besitzt
-
einen Unique Identifier (üblicherweise der Primary Key des
Datensatzes aus der Datenbank),
-
Properties (die Werte der Spalten aus der zugehörigen
Tabelle) und
-
Referenzen auf andere Beans (die Beziehungen innerhalb des
relationalen Modells).
Exemplare einer Bean können von Hibernate persistent gemacht werden, müssen es aber nicht. Die Beans lassen sich auch unabhängig von Hibernate verwenden und entwickeln. Das unterscheidet Hibernate von Technologien wie Container Managed Persistence im J2EE-EJB-Kontext (siehe[3]), bei denen die Beans einen dafür vorgesehenen Container brauchen und spezielle Interfaces implementieren (oder noch schlimmer: spezielle Klassen erweitern) müssen. Im Gegensatz dazu verfolgt Hibernate den Ansatz eines passiven Persistenz-Service, der die Objekte nicht selbst verwaltet.
Bean-Schnittstelle
Listing 1 zeigt eine solche Bean für Adressdaten. Seine Properties müssen nicht als »public« deklariert sein, wie es oft bei anderen ORM-Tools der Fall ist. Hibernate setzt sie über entsprechende Setter- und Getter-Methoden.
01 package com.freiheit.beans;
02 import java.io.Serializable;
03
04 public class Address implements Serializable {
05
06 private Integer _id;
07 private String _firstname;
08 private Country _country;
09 private Customer _customer;
10
11 /* ... weitere Properties ... */
12
13 public Address() {
14 }
15
16 public Integer getId() {
17 return _id;
18 }
19 public void setId(Integer id) {
20 _id = id;
21 }
22
23 public String getFirstname() {
24 return _firstname;
25 }
26 public void setFirstname(String firstname) {
27 _firstname = firstname;
28 }
29
30 /* ... weitere Getter und Setter ... */
31 }
|
Alle Java-Beans, die Hibernate persistent machen soll, registriert der Programmierer bei einer »SessionFactory«. Sie sollte pro Datenbank als Singleton existieren, siehe Listing 2, Zeile 10. Mit ihrer Hilfe erzeugt er Session-Objekte, über die die eigentlichen Datenbankoperationen laufen, optional als Transaktionen. Verbindungen (Connections) erhält die »SessionFactory« von einem »ConnectionProvider«, einem Adapter, der intern meist einen Connection-Pool verwaltet.
01 Configuration configuration = new Configuration()
02 .addClass( Address.class )
03 .addClass( Bank.class )
04 .addClass( CreditCardCompany.class )
05 .addClass( PaymentMethod.class )
06 .addClass( Country.class )
07 .addClass( Customer.class )
08 .addClass( Keyword.class );
09
10 SessionFactory factory = configuration. buildSessionFactory();
|
Hibernate bringt eine Vielzahl von Adaptern mit. Für den Produktivbetrieb hat sich wegen seiner Stabilität und der sehr guten Konfigurationsmöglichkeiten der DBCP aus dem Apache-Projekt als erste Wahl erwiesen. Dieser und alle übrigen mitgelieferten Adapter werden über eine einfache Property-Datei konfiguriert. Sofern nicht anders vorgegeben, liest Hibernate die Datei »hibernate.properties« in der Wurzel des »CLASS- PATH«, wenn es das Configuration-Objekt erzeugt. Wer diesen Mechanismus nicht verwenden möchte, übergibt die gewünschten Optionen manuell in einem Properties-Objekt.