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

47.2 Datenbanktabellen und Java-Objekte



47.2.1 Eine einfache Java-Klasse für Tabellen

Wenn wir uns an die Tabelle dir aus Kapitel 44 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 hier noch einmal die Tabellenstruktur:

Name Typ Bedeutung
did INT Primärschlüssel
dname CHAR(100) Verzeichnisname
fatherdid INT Schlüssel Vaterverzeichnis
entries INT Anzahl der Verzeichniseinträge

Tabelle 47.1: Die Struktur der dir-Tabelle

Der Einfachheit halber wollen wir uns im ersten Teil des Kapitels auf die Tabelle dir der Datenbank konzentrieren. 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 /* Verzeichnis.java */
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 public class Verzeichnis {
009 
010   // Variablen die den Attributen der Tabelle entsprechen
011   private int did;
012   private String dname;
013   private int fatherid;
014   private int entries;
015 
016   /**
017    * Ein einfacher Konstruktor ohne Initialisierung der
018    * Objektvariablen (Minimalkonstruktor)
019    */
020   public Verzeichnis() {
021   }
022 
023   /**
024    * Konstruktor zum Erzeugen von Instanzen der Klasse
025    */
026   public Verzeichnis(int did,
027                    String dname,
028                    int fatherid,
029                    int entries)
030   {
031     this.did = did;
032     this.dname = dname;
033     this.fatherid = fatherid;
034     this.entries = entries;
035   }
036 
037   // Zugriffsmethoden, um die Daten
038   // Lesen und Schreiben zu können  
039   public int getDid()
040   {
041     return did;
042   }
043 
044   public void setDid(int did)
045   {
046     this.did = did;
047   }
048 
049   public String getDname()
050   {
051     return dname;
052   }
053 
054   public void setDname(String dname)
055   {
056     this.dname = dname;
057   }
058 
059   public int getFatherid()
060   {
061     return fatherid;
062   }
063 
064   public void setFatherid(int fatherid)
065   {
066     this.fatherid = fatherid;
067   }
068 
069   public int getEntries()
070   {
071     return entries;
072   }
073 
074   public void setEntries(int entries)
075   {
076     this.entries = entries;
077   }
078 
079   public String toString() 
080   {
081     return "Directory[id:"+ did + ", name:" + dname +  "]";
082   }    
083 }
Listing 47.1: Eine Klasse für die dir-Tabelle

Die Klasse Verzeichnis enthält 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 wurden in Abschnitt 46.1 beschrieben.

Egal welche Konstruktoren für die mit der Datenbank verknüpfte Java Bean vorgesehen sind: Das Persistenz-Framework benötigt immer einen parameterlosen Standardkonstruktor. Wenn nötig, kann man dessen Sichtbarkeit über den Modifier protected einschränken.

 Warnung 

47.2.2 Verknüpfen der Java-Klasse mit der Datenbank

Die soeben erstellte Java-Klasse entspricht auf triviale Weise der Datenbanktabelle dir. Um sie mit dem Persistenz-Framework zu verbinden, bedienen wir uns zusätzlicher Metainformationen in Form von Annotationen (siehe auch Abschnitt 45.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 45 - 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:

001 /* Verzeichnis.java */
002 
003 import javax.persistence.*;
004 
005 /**
006  * Diese Klasse repräsentiert die Tabelle 'dir' der 'DirDB'
007  * Jede Instanz der Klasse repräsentiert wiederum einen
008  * Datensatz
009  */
010 @Entity
011 @Table( name = "dir" )
012 public class Verzeichnis {
013 
014   // Variablen die den Attributen der Tabelle entsprechen
015   private int did;
016   private String dname;
017   private int fatherid;
018   private int entries;
019 
020   /**
021    * Ein einfacher Konstruktor ohne Initialisierung der
022    * Objektvariablen
023    */
024   public Verzeichnis() {
025   }
026 
027   /**
028    * Konstruktor mit Initialisierung der Variablen
029    */
030   public Verzeichnis(int did,
031                    String dname,
032                    int fatherid,
033                    int entries)
034   {
035     this.did = did;
036     this.dname = dname;
037     this.fatherid = fatherid;
038     this.entries = entries;
039   }
040 
041   // Zugriffsmethoden, um die Daten der Klasse
042   // Auslesen und Schreiben zu können
043   @Id
044   @Column( name = "id" )
045   public int getDid()
046   {
047     return did;
048   }
049 
050   public void setDid(int did)
051   {
052     this.did = did;
053   }
054 
055   @Column( name = "dname", nullable = false ) 
056   public String getDname()
057   {
058     return dname;
059   }
060 
061   public void setDname(String dname)
062   {
063     this.dname = dname;
064   }
065 
066   @Column ( name = "fatherid" )
067   public int getFatherid()
068   {
069     return fatherid;
070   }
071 
072   public void setFatherid(int fatherid)
073   {
074     this.fatherid = fatherid;
075   }
076 
077   @Column ( name = "entries" )
078   public int getEntries()
079   {
080     return entries;
081   }
082 
083   public void setEntries(int entries)
084   {
085     this.entries = entries;
086   }
087     
088   public String toString() 
089   {
090     return "Directory[id:"+ did + ", name:" + dname +  "]";
091   }    
092 }
Listing 47.2: Annotierte Klasse für die dir-Tabelle

Wenn wir Listing 47.2 mit Listing 47.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. Diese Annotationen können entweder über den Attributen der Klasse selbst oder über den damit verknüpften Getter-Methoden stehen. Die hier verwendeten Annotationen haben folgende Bedeutung:

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 47.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 identisch mit dem Namen des Java-Attributs, kann die Angabe auch weggelassen werden, aus Gründen der Dokumentation ist das normalerweise aber nicht empfehlenswert. Zeile 055 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 beispielsweise 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 47.3: Attribute der Annotation @Column

47.2.3 Konfiguration des Datenbankzugriffs

Wir haben nun eine Java Bean erstellt, die in der Lage ist, einen Datensatz der Tabelle dir aufzunehmen. Anschließend haben wir sie mit Zusatzinformationen versehen, um die Verknüpfung zwischen Datenbank und Java-Klasse zu beschreiben. Nun fehlt nur noch der Hinweis, in welcher Datenbank sich die entsprechenden Tabellen befinden.

Natürlich könnten diese Informationen auch in der Java-Klasse selbst abgelegt werden, das würde aber 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, die als Persistence Descriptor bezeichnet wird.

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>Verzeichnis</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 47.3: Konfigurationsdatei für das Java Persistenz API

Diese Datei muss unter dem Namen persistence.xml im Klassenpfad abgelegt werden, damit das Persistenz API die Klasse Verzeichnis mit der Tabelle dir in der HSQLDB-Datenbank verknüpfen kann. Am einfachsten ist dies zu bewerkstelligen, indem die Datei persistence.xml im selben Verzeichnis gespeichert wird, in dem auch die kompilierte Class-Datei Verzeichnis.class zu finden ist.

Aufbau der Konfigurationsdatei

Die Konfigurationsdatei ist in einzelne Segmente aufgeteilt, die verschiedene Aufgaben haben. Listing 47.3 ist so vorkonfiguriert, dass es mit der HSQLDB-Datenbank aus Kapitel 44 verwendet werden kann. Um auf die Tabellen einer anderen Datenbank zuzugreifen, müssen der Datenbanktreiber, die URL und die Zugangsdaten angepasst werden. Vergleicht man dieses Listing mit Listing 44.3, kommen einem viele Optionen vertraut vor. Sie 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 47.4: Anpassen der Konfigurationsdatei

Die vorangegangenen Schritte erscheinen auf den ersten Blick aufwändiger als die korrespondierenden Pendants in JDBC. Der Vorteil des Java Persistenz API liegt jedoch vor allem in der wesentlich einfacheren Anwendung im Programmcode. Nachdem wir die Verknüpfung zwischen Java-Klasse und Datenbank konfiguriert haben, können wir einfach mit der Verzeichnis-Klasse arbeiten wie mit jeder anderen Java-Klasse auch, ohne uns weiter um SQL oder technische Aspekte der Datenbank kümmern zu müssen.


 Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 7. Auflage, Addison Wesley, Version 7.0
 <<    <     >    >>   API  © 1998, 2011 Guido Krüger & Heiko Hansen, http://www.javabuch.de