Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 6. Auflage
 <<    <     >    >>   API  Kapitel 45 - Objektorientierte Persistenz

45.2 Datenbanktabellen und Java-Objekte



45.2.1 Eine einfache Java-Klasse für Tabellen

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 abgebildet werden kann, deren Instanzen die Datensätze repräsentieren. Zur Erinnerung - und damit Sie nicht immer hin- und herblättern müssen - ist in Tabelle 45.1 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 im ersten Teil des Kapitels auf die Tabelle dir der Datenbank konzentrieren, obwohl wie wir sehen werden die Java Bean für die Tabelle file ganz analog aussieht. Hierfür entwerfen wir zunächst eine einfache Java-Klasse mit Variablen, die den Attributen der Datenbanktabelle 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 (Minimalkonstruktor)
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 }
Listing 45.1: Eine Klasse für die dir-Tabelle

Wie wir sehen enthält die Klasse Directory für jedes Datenbankattribut eine äquivalente Variable, die über Getter-Methoden ausgelesen und über Setter-Methoden verändert werden kann. Derartige Java-Objekte werden auch als Java Beans bezeichnet und in Abschnitt 44.1 beschrieben.

45.2.2 Verknüpfen der Java-Klasse mit der Datenbank

Die soeben erstellte Java-Klasse ist sehr einfach und entspricht auf triviale Weise der Datenbanktabelle dir. Diese Verknüpfung werden wir nun dem Persistenz-Framework anzeigen. Hierzu bedienen wir uns zusätzlicher Metainformationen in Form sogenannter Annotationen. Mehr zu Annotationen finden Sie in Abschnitt 43.6.

Die Metainformationen beeinflussen die Klasse oder den Programmablauf in keiner Weise, können jedoch zur Laufzeit - zum Beispiel über das Reflection API aus Kapitel 43 - ausgelesen werden. Die in den Annotationen hinterlegten Informationen teilen der Persistenzschicht mit, welche Tabelle in der Datenbank und welche Spalten durch die Attribute der Java Bean abgebildet werden. Listing 45.2 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
Listing 45.2: Annotierte Klasse für die dir-Tabelle

Wenn wir Listing 45.2 mit Listing 45.1 vergleichen stellen wir fest, 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 Attributen der Klasse selbst oder über den damit verknüpften Getter-Methoden stehen. Die Reihenfolge der Methoden spielt keine Rolle.

Tabelle 45.2 beschreibt die Bedeutung der verwendeten Annotationen:

Annotation Beschreibung
@Entity Markiert die Klasse als persistierbares, das heißt mit einer Datenbank verknüpftes Objekt
@Table Bezeichnet die verknüpfte Datenbanktabelle
@Id Markiert das Attribut als Primärschlüssel der Datenbank
@Column Verknüpft das Attribut mit einer Spalte in der Datenbanktabelle

Tabelle 45.2: Die Struktur der dir-Tabelle

Die Annotationen @Table und @Column besitzen jeweils das Attribut name, das den Namen der verknüpften Datenbanktabelle bzw. -spalte enthält. Ist dieser Name identisch mit dem Namen des Java-Attributs 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 beschrieben werden können.

Die Annotation @Column unterstützt die in Tabelle 45.3 beschriebenen 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 Sie für die mit der Datenbank verknüpfte Java Bean vorsehen: Das Persistenz-Framework benötigt zusätzlich immer einen parameterlosen Standardkonstruktor. Bei Bedarf können Sie dessen Sichtbarkeit auch über den Modifier protected einschränken, er muss jedoch vorhanden sein.

 Warnung 

45.2.3 Konfiguration des Datenbankzugriffs

Jetzt haben wir eine Java Bean erstellt, die in der Lage ist einen Datensatz der Tabelle dir aufzunehmen und diese anschließend mit Zusatzinformationen ausgestattet, um die Verknüpfung zwischen Datenbank und Java-Klasse 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 Java-Klasse abgelegt werden, dies würde jedoch zu unflexiblem Code führen, der nicht mit verschiedenen Datenbanken zusammenarbeiten würde. Um die Datenbank auch im Nachhinein flexibel austauschen und beispielsweise statt der HSQLDB eine Access oder MySQL-Datenbank verwenden zu können, werden diese Konfigurationsdaten in einer separaten Datei gespeichert. Diese wird auch als Persistence Descriptor bezeichnet.

001 <?xml version="1.0" encoding="ISO-8859-1"?>
002 
003 <!-- Persistence 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 
040       <!-- Flag, ob SQL-Statements formatiert werden sollen -->
041       <property name="hibernate.format_sql" value="true"/> 
042     </properties>
043   </persistence-unit>
044 </persistence>
persistence.xml
Listing 45.3: Konfigurationsdatei für das Java Persistenz API

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.

Aufbau der Konfigurationsdatei

Die Konfigurationsdatei ist in einzelne Segmente aufgeteilt, die verschiedene Aufgaben haben. Listing 45.3 ist so vorkonfiguriert, dass es mit der HSQLDB aus Kapitel 42 verwendet werden kann. Um auf die Tabellen einer anderen Datenbank zuzugreifen müssen der Datenbanktreiber, die URL und die Zugangsdaten angepasst werden. Wenn Sie dieses Listing mit Listing 42.3 vergleichen, sollten Ihnen viele Optionen vertraut vorkommen. Diese sind nun aber nicht mehr fest in die Java-Klasse einkompiliert, sondern können in einer separaten Datei gewartet werden.

Zeile Beschreibung der Konfigurationseinstellung
Zeile 007 Ein symbolischer Name für die Konfiguration, der später für den Zugriff verwenden wird
Zeile 008 Steuerung der Transaktionen auf SQL-Ebene. Erlaubte Werte sind RESOURCE_LOCAL und JTA (Standardwert). Für die Verwendung mit der Standard Edition ist RESOURCE_LOCAL notwendig
Zeile 014 Liste der Klassen, die mit der Datenbank verknüpft werden sollen
Zeile 020 Name des passenden mit der Datenbank kompatiblen 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 zur Laufzeit erzeugt werden sollen
Zeile 038 Gibt an, ob die intern ausgeführten SQL-Statements auf der Kommandozeile ausgegeben werden sollen
Zeile 041 Gibt an, ob die intern ausgeführten SQL-Statements formatiert 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

Der in diesem Abschnitt verwendete Persistence Descriptor (persistence.xml) ist für die Verwendung mit der Java Standard Edition und damit außerhalb eines Java EE Application Servers konfiguriert. Wenn Sie die JPA in einer Java-EE-Umgebung einsetzen möchten, müssen Sie den transaction-type in Zeile 008 auf JTA setzen.

 Warnung 

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 Java-Klasse und Datenbank nun konfiguriert haben, können wir nachfolgend einfach mit der Directory-Klasse arbeiten, ohne uns weiter um SQL oder Aspekte der Datenbank kümmern zu müssen.

 Hinweis 


 Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 6. Auflage, Addison Wesley, Version 6.0
 <<    <     >    >>   API  © 1998, 2009 Guido Krüger & Thomas Stark, http://www.javabuch.de