Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 5. Auflage |
<< | < | > | >> | API | Kapitel 45 - Objektorientierte Persistenz |
Wenn wir uns an die Tabelle dir aus Kapitel 42 erinnern, könnten wir auf die Idee kommen, dass diese auch durch eine Java Bean repräsentiert werden kann, deren Instanzen die Datensätze repräsentieren. Zur Erinnerung - und damit Sie nicht immer hin- und herblättern müssen - sei hier noch einmal die Tabellenstruktur angeführt:
Name | Typ | Bedeutung |
did | INT | Primärschlüssel |
dname | CHAR(100) | Verzeichnisname |
fatherdid | INT | Schlüssel Vaterverzeichnis |
entries | INT | Anzahl der Verzeichniseinträge |
Tabelle 45.1: Die Struktur der dir-Tabelle
Der Einfachheit halber wollen wir uns hier auf die Tabelle dir der Datenbank konzentrieren, obwohl die Java Bean für die Klasse file ganz analog wäre. Hierfür entwerfen wir zunächst eine einfache Javaklasse mit Variablen, die den Attributen der Datenbank-Tabelle entsprechen. Jede Instanz der Klasse kann damit eine Zeile bzw. einen Datensatz der Tabelle repräsentieren.
001 /** 002 * Diese Klasse repräsentiert die Tabelle 'dir' der 'DirDB' 003 * Jede Instanz der Klasse repräsentiert wiederum einen 004 * Datensatz 005 */ 006 public class Directory { 007 008 // Variablen die den Attributen der Tabelle entsprechen 009 private int did; 010 private String dname; 011 private int fatherid; 012 private int entries; 013 014 /** 015 * Ein einfacher Konstruktor ohne Initialisierung der 016 * Objektvariablen 017 */ 018 public Directory() { 019 } 020 021 /** 022 * Konstruktor zum Erzeugen von Instanzen der Klasse 023 */ 024 public Directory(int did, 025 String dname, 026 int fatherid, 027 int entries) 028 { 029 this.did = did; 030 this.dname = dname; 031 this.fatherid = fatherid; 032 this.entries = entries; 033 } 034 035 // Zugriffsmethoden, um die Daten 036 // Lesen und Schreiben zu können 037 public int getDid() 038 { 039 return did; 040 } 041 042 public void setDid(int did) 043 { 044 this.did = did; 045 } 046 047 public String getDname() 048 { 049 return dname; 050 } 051 052 public void setDname(String dname) 053 { 054 this.dname = dname; 055 } 056 057 public int getFatherid() 058 { 059 return fatherid; 060 } 061 062 public void setFatherid(int fatherid) 063 { 064 this.fatherid = fatherid; 065 } 066 067 public int getEntries() 068 { 069 return entries; 070 } 071 072 public void setEntries(int entries) 073 { 074 this.entries = entries; 075 } 076 077 public String toString() 078 { 079 return "Directory[id:"+ did + ", name:" + dname + "]"; 080 } 081 } |
Wie wir sehen enthält die Klasse Directory für jedes Datenbank-Attribut eine äquivalente Variable, die über Getter-Methoden ausgelesen und über Setter-Methoden verändert werden kann. Derartige Javaobjekte werden auch als Java Beans bezeichnet, die wir in Abschnitt 44.1 kennen gelernt haben.
Die soeben erstellte Javaklasse ist sehr einfach und entspricht auf triviale Weise der Datenbank-Tabelle, jedoch müssen wir diese Verknüpfung Java auch direkt und unmissverständlich anzeigen. Hierzu bedienen wir uns zusätzlicher Metainformationen in Form so genannter Annotationen, die in Abschnitt 43.6 beschrieben wurden.
Diese Metainformationen beeinflussen die Klasse oder den Programmablauf in keiner Weise, können jedoch zur Laufzeit - zum Beispiel über die Reflection API - ausgelesen werden. Die in den Annotationen hinterlegten Informationen teilen der Persistenzschicht dabei mit, welche Tabelle der Datenbank und welche Spalten auf die jeweiligen Attribute abgebildet werden. Das folgende Listing zeigt die hierfür notwendigen Erweiterungen:
001 import javax.persistence.*; 002 003 /** 004 * Diese Klasse repräsentiert die Tabelle 'dir' der 'DirDB' 005 * Jede Instanz der Klasse repräsentiert wiederum einen 006 * Datensatz 007 */ 008 @Entity 009 @Table( name = "dir" ) 010 public class Directory { 011 012 // Variablen die den Attributen der Tabelle entsprechen 013 private int did; 014 private String dname; 015 private int fatherid; 016 private int entries; 017 018 /** 019 * Ein einfacher Konstruktor ohne Initialisierung der 020 * Objektvariablen 021 */ 022 public Directory() { 023 } 024 025 /** 026 * Konstruktor mit Initialisierung der Variablen 027 */ 028 public Directory(int did, 029 String dname, 030 int fatherid, 031 int entries) 032 { 033 this.did = did; 034 this.dname = dname; 035 this.fatherid = fatherid; 036 this.entries = entries; 037 } 038 039 // Zugriffsmethoden, um die Daten der Klasse 040 // Auslesen und Schreiben zu können 041 @Id 042 @Column( name = "id" ) 043 public int getDid() 044 { 045 return did; 046 } 047 048 public void setDid(int did) 049 { 050 this.did = did; 051 } 052 053 @Column( name = "dname", nullable = false ) 054 public String getDname() 055 { 056 return dname; 057 } 058 059 public void setDname(String dname) 060 { 061 this.dname = dname; 062 } 063 064 @Column ( name = "fatherid" ) 065 public int getFatherid() 066 { 067 return fatherid; 068 } 069 070 public void setFatherid(int fatherid) 071 { 072 this.fatherid = fatherid; 073 } 074 075 @Column ( name = "entries" ) 076 public int getEntries() 077 { 078 return entries; 079 } 080 081 public void setEntries(int entries) 082 { 083 this.entries = entries; 084 } 085 086 public String toString() 087 { 088 return "Directory[id:"+ did + ", name:" + dname + "]"; 089 } 090 } |
Directory.java |
Wenn wir dieses Listing mit dem vorangegangenen vergleichen, sehen wir, dass lediglich einige Annotationen hinzugekommen sind. Sie enthalten Informationen, die Java benötigt, um die Instanzen der Klasse mit der Tabelle in der Datenbank zu verknüpfen. Die Annotationen für das Java Persistenz API können entweder über den Variablen der Klasse selbst oder über die damit verknüpften Getter-Methoden stehen. Die Reihenfolge der Methoden spielt keine Rolle.
Die Annotationen haben folgende Bedeutung:
Annotation | Beschreibung |
@Entity | Markiert die Klasse als persistierbares, dass heißt mit einer Datenbank verknüpftes Objekt |
@Table | Bezeichnet die verknüpfte Datenbanktabelle |
@Id | Markiert das Attribut als Primärschlüssel der Datenbank. Das ist z.B. für Suchoperationen wichtig |
@Column | Verknüpft das Attribut mit einer Datenbankspalte |
Tabelle 45.2: Die Struktur der dir-Tabelle
Die Annotationen Table und Column besitzen jeweils das Attribut name, das den Namen der verknüpften Tabelle bzw. Spalte enthält. Ist dieser Name identisch mit dem Namen des Javaattributes kann die Angabe auch weggelassen werden. Allerdings empfehlen wir Ihnen - schon allein um die Dokumentation zu erhöhen - diese Angabe mit aufzunehmen. Zeile 053 zeigt zudem, dass die Annotationen weitere Attribute aufnehmen können, mit denen die Struktur und Beschränkungen der Datenbank granular konfiguriert werden können.
Die Annotation @Column unterstützt folgende Attribute
Attribut | Typ | Beschreibung | Standardwert |
name | String | Name der Tabellenspalte | Name des Java Bean Attributs |
length | int | Maximale Länge des Eintrags | 255 |
table | String | Name einer Tabelle | Namen der Tabelle dieser Java Bean |
nullable | boolean | Sind null-Werte erlaubt? | true |
insertable | boolean | Darf dieser Wert mit INSERT Statements verändert werden? | true |
updateable | boolean | Darf dieser Wert mit UPDATE Statements geändert werden? | true |
Tabelle 45.3: Attribute der Annotation @Column
![]() |
![]() |
![]() |
![]() |
Egal welche Konstruktoren wir für die mit der Datenbank verknüpfte Java Bean vorsehen, das Persistenz Framework benötigt zusätzlich einen »leeren« Standardkonstruktor. Bei Bedarf können Sie dessen Sichtbarkeit auch auf protected setzen und damit für andere Klassen weitestgehend einschränken. Er muss jedoch vorhanden sein, da das Framework sonst nicht mit der Java Bean arbeiten kann! |
![]() |
|
![]() |
Jetzt haben wir eine Java Bean erstellt, die in der Lage, ist einen Datensatz der Tabelle dir aufzunehmen und diese zudem mit Zusatzinformationen ausgestattet, um die Verknüpfung zwischen Datenbank auf Javaklasse beschreiben. Was noch fehlt sind Informationen darüber, in welcher Datenbank sich die entsprechenden Tabellen befinden.
Natürlich könnten diese Informationen theoretisch ebenfalls in der Javaklasse abgelegt werden, dies würde jedoch zu unflexiblem Code führen, der nicht mit verschiedenen Datenbanken zusammenarbeiten könnte. Um die tatsächliche Datenbank auch im Nachhinein flexibel austauschen und beispielsweise statt der Hypersonic DB eine Access Datenbank verwenden zu können, werden diese Konfigurationsdaten in einer separaten Datei gepflegt. Diese wird auch als Persistence Descriptor bezeichnet und könnte beispielsweise folgende Form haben:
001 <?xml version="1.0" encoding="ISO-8859-1"?> 002 003 <!-- Persistenz Descriptor zur Konfiguration --> 004 <persistence> 005 006 <!-- Hinterlegen eines symbolischen Namens --> 007 <persistence-unit name="persistenceExample" 008 transaction-type="RESOURCE_LOCAL"> 009 010 <!-- Zu verwendende Implementierung --> 011 <provider>org.hibernate.ejb.HibernatePersistence</provider> 012 013 <!-- Persistierbare Klassen --> 014 <class>Directory</class> 015 016 <!-- Konfiguration der Hibernate Implementierung --> 017 <properties> 018 <!-- Name des intern verwendeten JDBC-Treibers --> 019 <property name="hibernate.connection.driver_class" 020 value="org.hsqldb.jdbcDriver"/> 021 022 <!-- URL der zu verwendenden Datenbank --> 023 <property name="hibernate.connection.url" 024 value="jdbc:hsqldb:hsqldbtest"/> 025 026 <!-- SQL-Dialect, den Hibernate verwenden soll --> 027 <property name="hibernate.dialect" 028 value="org.hibernate.dialect.HSQLDialect"/> 029 030 <!-- Benutzername und Passwort; Standardwerte der HSQLDB --> 031 <property name="hibernate.connection.username" value="SA"/> 032 <property name="hibernate.connection.password" value=""/> 033 034 <!-- Flag, ob Tabellen automatisch erzeugt werden sollen --> 035 <property name="hibernate.hbm2ddl.auto" value="create"/> 036 037 <!-- Flag, ob SQL-Statements ausgegeben werden sollen --> 038 <property name="hibernate.show_sql" value="true"/> 039 </properties> 040 </persistence-unit> 041 </persistence> |
persistence.xml |
Diese Datei muss unter dem Namen persistence.xml im Classpath abgelegt werden, und schon kann das Persistenz API die Klasse Directory mit der Tabelle dir in der HSQLDB verknüpfen. Am einfachsten ist dies zu bewerkstelligen, indem die Datei persistence.xml gemeinsam mit der kompilierten Class-Datei Directory.class gespeichert wird.
Die Konfigurationsdatei ist in einzelne Seqmente aufgeteilt, die verschiedene Aufgaben besitzen. Listing 45.3 ist so vorkonfiguriert, dass es mit der HSQLDB aus Kapitel Kapitel 42 verwenden werden kann. Um auf die Tabellen einer anderen Datenbank zuzugreifen müssen der Datenbanktreiber, die URL und die Zugangsdaten angepasst werden. Wenn wir dieses Listing mit Listing 42.3 vergleichen, sollten uns viele Optionen vertraut vorkommen. Diese sind nun nicht mehr fest in die Javaklasse einkompiliert, sondern können in einer separaten Datei gewartet werden.
Zeile | Beschreibung der Konfigurationseinstellung |
Zeile 007 | Ein symbolischer Name für die Konfiguration, den wir später für den Zugriff verwenden werden |
Zeile 014 | Liste der Klassen, die mit der Datenbank verknüpft werden sollen |
Zeile 020 | Name des passenden JDBC-Treibers |
Zeile 024 | Name der Datenbank |
Zeile 031 | Benutzername für den Zugriff auf die Datenbank |
Zeile 032 | Passwort für den Zugriff auf die Datenbank |
Zeile 035 | Gibt an, ob die Tabellen bei Bedarf dynamisch erstellt werden sollen |
Zeile 038 | Gibt an, ob die intern verwendeten SQL-Statements auf der Kommandozeile ausgegeben werden sollen |
Tabelle 45.4: Anpassen der Konfigurationsdatei
Nachdem wir die Datei persistence.xml zusammen mit der Directory-Class abgelegt haben können wir nun mit dem Java Persistenz API arbeiten
![]() |
![]() |
Die vorangegangenen Schritte erscheinen Ihnen vielleicht aufwändiger als die vermeintlichen Pendants im Kapitel über JDBC. Der Vorteil des Java Persistenz API liegt jedoch vor allem in der wesentlich einfacheren Anwendung, mit der wir uns im folgenden Abschnitt beschäftigen werden. Die gute Nachricht ist: Nachdem wir die Verknüpfung zwischen Javaklasse und Datenbank nun konfiguriert haben, können wir nachfolgend einfach mit der Directory-Klasse arbeiten, ohne uns weiter um SQL oder Datenbank-Aspekte kümmern zu müssen. |
![]() |
|
![]() |
Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 5. Auflage, Addison Wesley, Version 5.0.1 |
<< | < | > | >> | API | © 1998, 2007 Guido Krüger & Thomas Stark, http://www.javabuch.de |