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

45.5 Objektorientierte Datenbankabfragen



Neben den Basisoperationen Anlegen, Speichern und Löschen eines Datensatzes ist die Suche nach Datensätzen eine weitere Hauptaufgabe der Datenbankzugriffsschicht. In Abschnitt 42.4.7 haben Sie hierfür die Datenbankanfragesprache SQL kennengelernt. Mit der sogenannten JPA Query Language steht eine solche Anfragesprache auch für die objektorientierte Zugriffsschicht zur Verfügung.

45.5.1 Suche nach Datensätzen

Die Anfragesprache für Persistence Beans orientiert sich stark an SQL und wird wie diese über ein spezielles Anfrageobjekt Query ausgeführt. Wenn Sie beispielsweise Listing 45.16 erfolgreich ausgeführt und die Datensätze für das Directory und die zwei anhängenden File-Objekte erstellt haben, können Sie nun mit Hilfe des Query-Objekts in Listing 45.17 die abgespeicherten Verzeichnisse auslesen und beispielsweise auf der Kommandozeile ausgeben.

001 /* Listing4517.java */
002 
003 import javax.persistence.*;
004 
005 public class Listing4517
006 {
007     public static void main(String[] args)
008     {
009         //Erzeugen einer EntityManagerFactory mit Hilfe des symbolischen
010         //Namens aus dem Persistence Descriptor (persistence.xml)
011         EntityManagerFactory emf =
012                 Persistence.createEntityManagerFactory("persistenceExample");
013 
014         //Erzeugen eines EntityManagers für den Zugriff auf
015         //die Datenbank
016         EntityManager manager = emf.createEntityManager();
017 
018         //Beginn einer neuen Transanktion
019         EntityTransaction tx = manager.getTransaction();
020         tx.begin();
021 
022         // Erzeugen einer Anfrage
023         Query query = manager.createQuery("select d from Directory d"); 
024 
025         // Ausführen der Anfrage und ermittel der Ergebnisliste
026         List<Directory> directories = query.getResultList();            
027         
028         // Ausgabe der Datensätze
029         for(Directory directory : directories) {
030             System.out.println(directory.toString());
031         }
032         
033         //Abschluss der Transaktion mit einem Commit
034         tx.commit();
035         
036         //Freigabe der Ressourcen des EntityManagers
037         manager.close();
038 
039         //Schließen der EntityManagerFactory und Freigeben der
040         //belegten Ressourcen
041         emf.close();
042     }
043 }
Listing4517.java
Listing 45.17: Suche nach Datensätzen

Das Query-Objekt in Zeile 023 wird mit Hilfe einer SQL-ähnlichen Anfrage erzeugt und stellt das JPA-Pendant zu einem Statement dar, die Sie in Abschnitt 42.4.5 kennengelernt haben. Die allgemeine Syntax für SQL- und JPA-Anfragen finden Sie in Abschnitt 42.4.7. Im Unterschied zu den SQL-Anfragen operiert das Java Persistence API aber ausschließlich auf Java-Objekten wie Directory, während der Name der Datenbanktabelle keine Rolle spielt.

Durch Aufruf der Methode getResultList in Zeile 026 wird die Anfrage ausgeführt und das Ergebnis als Liste zurückgegeben. Hierbei muss der Typ der Liste natürlich zur formulierten Anfrage passen, da es sonst zu einer ClassCastException kommt.

45.5.2 Eigenschaftsbasierte Suche nach Datensätzen

Nun ist es bei einer umfangreichen Datenbank mühsam zunächst alle Datensätze einer Tabelle auszulesen und dann das gesuchte Objekt über eine Schleife zu ermitteln, weshalb sich die Suche über eine Where-Klausel zum Beispiel auf den Namen des Verzeichnisses einschränken lässt.

Ähnlich wie bei einem PreparedStatement in Abschnitt 42.4.6 verwenden wir hierfür einen Parameter als Platzhalter für den Namen des Verzeichnisses.

001 /* Listing4518.java */
002 
003 import javax.persistence.*;
004 
005 public class Listing4518
006 {
007     public static void main(String[] args)
008     {
009         //Erzeugen einer EntityManagerFactory mit Hilfe des symbolischen
010         //Namens aus dem Persistence Descriptor (persistence.xml)
011         EntityManagerFactory emf =
012                 Persistence.createEntityManagerFactory("persistenceExample");
013 
014         //Erzeugen eines EntityManagers für den Zugriff auf
015         //die Datenbank
016         EntityManager manager = emf.createEntityManager();
017 
018         //Beginn einer neuen Transanktion
019         EntityTransaction tx = manager.getTransaction();
020         tx.begin();
021 
022         // Erzeugen einer Anfrage mit dem Parameter :name
023         Query query =
024            manager.createQuery("select d from Directory d where d.name = :name"); 
025 
026         // Setzen des Parameters
027         query.setParameter("name", "temp");                        
028 
029         // Auslesen eines einzelnen Ergenisses
030         Directory directory = (Directory) query.getSingleResult(); 
031         System.out.println(directory.toString());
032 
033         //Abschluss der Transaktion mit einem Commit
034         tx.commit();
035 
036         //Freigabe der Ressourcen des EntityManagers
037         manager.close();
038 
039         //Schließen der EntityManagerFactory und Freigeben der
040         //belegten Ressourcen
041         emf.close();    
042     }
043 }
Listing4518.java
Listing 45.18: Suche nach Datensätzen mit einer Where-Klausel

In Zeile 024 wird eine Query mit einer Einschränkung auf den Namen des Verzeichnisses definiert. Dabei ist zu beachten, dass hierfür der Name des Java-Attributs (name) und nicht der Name der Spalte in der Datenbanktabelle (dname) verwendet wird. Der Name des Platzhalters ist frei wählbar und wird mit der Escape-Sequenz : eingeleitet.

In Zeile 027 wird der zuvor definierte Parameter schließlich mit Leben gefüllt. An dieser Stelle wird nur der Name des Parameters ohne führenden Doppelpunkt verwendet.

Um die Anfrage auszuführen, könnten wir nun analog zu Listing 45.17 die Methode getResultList aufrufen, die uns eine Liste der Länge 1 zurückgeben würde. Für Anfragen, die jedoch genau einen Datensatz als Ergebnis haben, definiert das Java Persistence API eine Abkürzung über die Methode getSingleResult in Zeile 030, die den gesuchten Datensatz direkt zurückgibt.

Wenn das Ergebnis der Anfrage aus mehr als einem oder keinem Datensatz besteht wirft die Methode getSingleResult eine Ausnahme.

 Warnung 

45.5.3 Definition von Standardanfragen

In einer komplexen Anwendung werden verschiedene Datenbankanfragen nicht nur an einer Stelle benötigt, sondern in verschiedenen Programmteilen gleichermaßen verwendet. Für diese Anwendungsfälle sieht das Java Persistence API die Definition von Standardanfragen in Form einer sogenannten NamedQuery vor.

Eine NamedQuery ist im Grunde nichts anders als eine (parametrisierte) Anfrage, die typischerweise an einer Persistence Bean definiert und anschließend nur noch über ihren symbolischen Namen referenziert wird. Listing 45.19 zeigt die Anfrage aus Abschnitt 45.5.2 als benannte Anfrage am Directory-Objekt.

001 /* Listing4519.java */
002 
003 import javax.persistence.*;
004 import java.util.List;
005 import java.util.LinkedList;
006 
007 /**
008  * Diese Klasse repräsentiert die Tabelle 'dir' der 'DirDB'
009  * Jede Instanz der Klasse repräsentiert wiederum einen
010  * Datensatz
011  */
012 @Entity
013 @Table( name = "dir" )
014 @NamedQuery(name = "DIRECTORY_BY_NAME",          
015             query = "select d from Directory d where d.name = :name")
016 public class Directory {
017 
018     @Id
019     @GeneratedValue
020     @Column(name = "did")
021     private Integer id;
022 
023     @Column(name = "dname")
024     private String name;
025 
026     @OneToMany(cascade = CascadeType.ALL, mappedBy = "directory")
027     @OrderBy(value = "name")
028     private List<File> files;
029 
030     /**
031      * Geschützter Minimalkonstruktor zur Verwendung von Hibernate
032      */
033     protected Directory() {
034     }
035 
036     /**
037      * Öffentlicher Konstruktor zur Verwendung durch den Entwickler.
038      * Dieser Konstruktor definiert alle Pflichtfelder der JavaBean.
039      * @param name - Name des Verzeichniseintrags
040      */
041     public Directory(String name)
042     {
043         this.name = name;
044         this.files = new LinkedList<File>();
045     }
046 
047     public Integer getId()
048     {
049         return id;
050     }
051     protected void setId(Integer id)
052     {
053         this.id = id;
054     }
055 
056     public String getName()
057     {
058         return name;
059     }
060     public void setName(String name)
061     {
062         this.name = name;
063     }
064 
065     public List<File> getFiles()
066     {
067         return files;
068     }
069     public void setFiles(List<File> files)
070     {
071         this.files = files;
072     }
073 
074     public boolean equals(Object o)
075     {
076         if (this == o) return true;
077         if (o == null || getClass() != o.getClass()) return false;
078 
079         Directory dir = (Directory) o;
080         return !(id != null ? !id.equals(dir.id) : dir.id != null);
081     }
082 
083     public int hashCode()
084     {
085         return id != null ? id.hashCode() : 0;
086     }
087 
088     public String toString()
089     {
090         return "Directory[id:"+ id + ", name:" + name +  "]";
091     }
092 }
Listing4519.java
Listing 45.19: Suche nach Datensätzen mit einer Where-Klausel

In Zeile Listing 45.19 wird die Anfrage aus Listing 45.18 zentral abgelegt und mit dem frei wählbaren, symbolischen Namen DIRECTORY_BY_NAME verknüpft. Hierfür wird die Annotation @NamedQuery verwendet. Um diese Anfrage ausführen zu können wird nun lediglich der Name der Anfrage benötigt.

Um mehrere benannte Anfragen an einer Entity zu definieren, fassen Sie die @NamedQuery-Annotationen einfach als Attribute einer einzelnen @NamedQueries-Annotation zusammen.

 Tipp 

001 /* Listing4520.java */
002 
003 import javax.persistence.*;
004 
005 public class Listing4520
006 {
007     public static void main(String[] args)
008     {
009         //Erzeugen einer EntityManagerFactory mit Hilfe des symbolischen
010         //Namens aus dem Persistence Descriptor (persistence.xml)
011         EntityManagerFactory emf =
012                 Persistence.createEntityManagerFactory("persistenceExample");
013 
014         //Erzeugen eines EntityManagers für den Zugriff auf
015         //die Datenbank
016         EntityManager manager = emf.createEntityManager();
017 
018         //Beginn einer neuen Transanktion
019         EntityTransaction tx = manager.getTransaction();
020         tx.begin();
021 
022         // Referenzieren der benannten Anfrage mit dem Parameter :name
023         Query query = manager.createNamedQuery("DIRECTORY_BY_NAME"); 
024 
025         // Setzen des Parameters
026         query.setParameter("name", "temp");
027 
028         // Auslesen eines einzelnen Ergebnisses
029         Directory directory = (Directory) query.getSingleResult();
030         System.out.println(directory.toString());
031 
032         //Abschluss der Transaktion mit einem Commit
033         tx.commit();
034 
035         //Freigabe der Ressourcen des EntityManagers
036         manager.close();
037 
038         //Schließen der EntityManagerFactory und Freigeben der
039         //belegten Ressourcen
040         emf.close();
041     }
042 }
Listing4520.java
Listing 45.20: Suche nach Datensätzen mit benannten Anfragen

In Zeile 023 wird beim Aufruf der Methode createNamedQuery der Name der benannten Anfrage synonym zur Anfrage verwendet. Anschließend werden die definierten Parameter gesetzt und die Anfrage schließlich mit den Methoden getSingleResult oder getResultList ausgeführt.


 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