Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 7. Auflage |
<< | < | > | >> | API | Kapitel 47 - Objektorientierte Persistenz |
Neben den Basisoperationen Anlegen, Speichern und Löschen eines Datensatzes ist die Suche nach Datensätzen eine weitere Hauptaufgabe der Datenbankzugriffsschicht. In Abschnitt 44.4.7 haben wir 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.
Die Anfragesprache für Persistenz Beans orientiert sich stark an SQL und wird über ein spezielles Anfrageobjekt Query ausgeführt. Nachdem beispielsweise Listing 47.16 erfolgreich ausgeführt und die Datensätze für das Verzeichnis und die zwei anhängenden Datei-Objekte erstellt wurden, können wir mit Hilfe eines Query-Objekts die abgespeicherten Verzeichnissätze auslesen und beispielsweise auf der Kommandozeile ausgeben:
001 /* Listing4717.java */ 002 003 import javax.persistence.*; 004 import java.util.List; 005 006 public class Listing4717 007 { 008 public static void main(String[] args) 009 { 010 //Erzeugen einer EntityManagerFactory mit Hilfe des symbolischen 011 //Namens aus dem Persistenz Descriptor (persistence.xml) 012 EntityManagerFactory emf = 013 Persistence.createEntityManagerFactory("persistenceExample"); 014 015 //Erzeugen eines EntityManagers für den Zugriff auf 016 //die Datenbank 017 EntityManager manager = emf.createEntityManager(); 018 019 //Beginn einer neuen Transanktion 020 EntityTransaction tx = manager.getTransaction(); 021 tx.begin(); 022 023 //Erzeugen einer Anfrage 024 Query query = manager.createQuery("select d from Verzeichnis d"); 025 026 //Ausführen der Anfrage und ermittel der Ergebnisliste 027 List<Verzeichnis> directories = query.getResultList(); 028 029 //Ausgabe der Datensätze 030 for(Verzeichnis directory : directories) { 031 System.out.println(directory.toString()); 032 } 033 034 //Abschluss der Transaktion mit einem Commit 035 tx.commit(); 036 037 //Freigabe der Ressourcen des EntityManagers 038 manager.close(); 039 040 //Schließen der EntityManagerFactory und Freigeben der 041 //belegten Ressourcen 042 emf.close(); 043 } 044 } |
Listing4717.java |
Das Query-Objekt in Zeile 024 wird mit Hilfe einer SQL-ähnlichen Anfrage erzeugt und stellt das JPA-Pendant zu einem Statement dar, wie wir sie in Abschnitt 44.4.5 kennengelernt haben. Die allgemeine Syntax für SQL- und JPA-Anfragen finden Sie in Abschnitt 44.4.7. Im Unterschied zu den SQL-Anfragen operiert das Java Persistenz API allerdings ausschließlich auf Java-Objekten (wie beispielsweise Verzeichnis) - der Name der Datenbanktabelle spielt keine Rolle.
Durch Aufruf der Methode getResultList in Zeile 027 wird die Anfrage ausgeführt und das Ergebnis als Liste zurückgegeben. Hierbei muss der Typ der Liste natürlich zur formulierten Anfrage passen, andernfalls kommt es zu einer ClassCastException.
Bei einer umfangreichen Datenbank ist es natürlich indiskutabel, zunächst alle Datensätze einer Tabelle auszulesen und dann das gewünschte Objekt in eine Schleife zu ermitteln. Deshalb lässt sich auch im JPA die Suche über eine Where-Klausel einschränken. Ähnlich wie bei einem PreparedStatement in Abschnitt 44.4.6 verwenden wir dazu einen Platzhalter als Parameter für die Elemente des Suchausdrucks:
001 /* Listing4718.java */ 002 003 import javax.persistence.*; 004 005 public class Listing4718 006 { 007 public static void main(String[] args) 008 { 009 //Erzeugen einer EntityManagerFactory mit Hilfe des symbolischen 010 //Namens aus dem Persistenz 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 = manager.createQuery( 024 "select d from Verzeichnis d where d.name = :name" 025 ); 026 027 //Setzen des Parameters 028 query.setParameter("name", "temp"); 029 030 //Auslesen eines einzelnen Ergenisses 031 Verzeichnis directory = (Verzeichnis) query.getSingleResult(); 032 System.out.println(directory.toString()); 033 034 //Abschluss der Transaktion mit einem Commit 035 tx.commit(); 036 037 //Freigabe der Ressourcen des EntityManagers 038 manager.close(); 039 040 //Schließen der EntityManagerFactory und Freigeben der 041 //belegten Ressourcen 042 emf.close(); 043 } 044 } |
Listing4718.java |
In Zeile 023 wird eine Query mit einer Einschränkung auf den Namen des Verzeichnisses definiert. Dabei ist zu beachten, dass 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 028 wird der zuvor definierte Parameter mit dem Wert "temp" gefüllt, der in der anschließenden Abfrage an seiner Stelle eingesetzt wird. Hier wird nur der Name des Parameters ohne führenden Doppelpunkt verwendet.
Um die Anfrage auszuführen, könnten wir nun analog zu Listing 47.17 die Methode getResultList aufrufen. Da wir nur einen Treffer erhalten, würde sie uns eine Liste der Länge 1 zurückgeben. Für diesen Fall definiert das Java Persistenz API mit der Methode getSingleResult eine Abkürzung, die den gesuchten Datensatz direkt zurückgibt.
In größeren Programmen werden Datenbankanfragen meist nicht nur an einer einzigen Stelle benötigt, sondern sie sollen in verschiedenen Programmteilen wiederverwendet werden. Für diese Anwendungsfälle sieht das JPA die Definition von Standardanfragen in Form einer sogenannten Named Query vor.
Eine NamedQuery ist im Grunde nichts anders als eine parametrisierte Anfrage, die an einer Persistenz Bean definiert und anschließend nur noch über ihren symbolischen Namen referenziert wird. Das folgende Listing zeigt die die Anfrage aus dem letzten Abschnitt als Named Query am Verzeichnis-Objekt:
001 /* Verzeichnis.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 Verzeichnis d where d.name = :name") 016 public class Verzeichnis 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<Datei> files; 029 030 /** 031 * Geschützter Minimalkonstruktor zur Verwendung von Hibernate 032 */ 033 protected Verzeichnis() { 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 Verzeichnis(String name) 042 { 043 this.name = name; 044 this.files = new LinkedList<Datei>(); 045 } 046 047 public Integer getId() 048 { 049 return id; 050 } 051 052 protected void setId(Integer id) 053 { 054 this.id = id; 055 } 056 057 public String getName() 058 { 059 return name; 060 } 061 062 public void setName(String name) 063 { 064 this.name = name; 065 } 066 067 public List<Datei> getFiles() 068 { 069 return files; 070 } 071 072 public void setFiles(List<Datei> files) 073 { 074 this.files = files; 075 } 076 077 public boolean equals(Object o) 078 { 079 if (this == o) return true; 080 if (o == null || getClass() != o.getClass()) return false; 081 082 Verzeichnis dir = (Verzeichnis) o; 083 return !(id != null ? !id.equals(dir.id) : dir.id != null); 084 } 085 086 public int hashCode() 087 { 088 return id != null ? id.hashCode() : 0; 089 } 090 091 public String toString() 092 { 093 return "Directory[id:"+ id + ", name:" + name + "]"; 094 } 095 } |
Verzeichnis.java |
In Zeile 014 wird die Anfrage mit Hilfe der Annotation @NamedQuery zentral definiert und mit dem (frei wählbaren) symbolischen Namen DIRECTORY_BY_NAME versehen. Das folgende Listing zeigt, wie die Named Query aufgerufen werden kann:
001 /* Listing4720.java */ 002 003 import javax.persistence.*; 004 005 public class Listing4720 006 { 007 public static void main(String[] args) 008 { 009 //Erzeugen einer EntityManagerFactory mit Hilfe des symbolischen 010 //Namens aus dem Persistenz 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 Verzeichnis directory = (Verzeichnis) 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 } |
Listing4720.java |
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 schließlich wird die Anfrage mit den Methoden getSingleResult oder getResultList ausgeführt.
Um mehrere benannte Anfragen an einer Entity zu definieren, fasst man die @NamedQuery-Annotationen einfach als Attribute einer einzelnen @NamedQueries-Annotation zusammen. |
|
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 |