
| HQL Hibernate Query Language. Objektorientierte Abfragesprache für relationale Datenbanken. |
| Entity Beans Java-Klassen, die z. B. mit Hilfe von Hibernate persistiert werden können. |
Weiterführende Links
Die Verwendung des Criteria API bietet sich an, wenn Suchabfragen dynamisch zusammengestellt werden müssen. Dies ist z. B. dann der Fall, wenn ein Benutzer bestimmte Suchkriterien eingibt, etwa durch Auswahl von Check-Boxen und Ausfüllen von Textfeldern. In der Anwendungslogik muss die Suchabfrage dann entsprechend der Eingabe des Benutzers durchgeführt werden. In HQL müsste der Entwickler den Such-String mühsam zusammensetzen. Bei dynamischen Abfragen kann dies sehr fehleranfällig und aufwändig werden, da die Schlüsselwörter, wie z. B. "from" oder "where", an den richtigen Stellen eingefügt werden müssen. Hier setzt das Criteria API an und bietet dem Entwickler Erleichterung: Die Suchabfrage kann sehr komfortabel durch Kombination von Objekten (Criterions) definiert werden.
List<Worker> workers= (List<Worker>)session.createCriteria(Worker.class).list(); |
| Abb. 1: Einfache Criteria-Abfrage. |
Um eine Criteria-Abfrage durchführen zu können, wird im einfachsten Fall die Methode createCriteria der Hibernate Session verwendet. Der Methode createCriteria wird als Argument die Entity Klasse mitgegeben, auf die sich die Abfrage bezieht. Eine einfache Abfrage zeigt das Beispiel in Abbildung 1.
Bei der dort dargestellten Abfrage wird bereits deutlich, dass Methoden bei Benutzung des Criteria API gerne verkettet werden. Die Methode list wird direkt auf die Methode createCriteria aufgerufen. Dies erhöht die Übersichtlichkeit, wie auch in den nachfolgenden Beispielen zu sehen ist. Zu beachten ist, dass die Abfragen, wie auch in der HQL, polymorph sind. Das bedeutet, die in Abbilung 1 vorgestellte Abfrage liefert auch alle abgeleiteten Entity Beans eines Mitarbeiters (Worker) zurück. Da Hibernate (noch) keine Generics unterstützt, ist ein Cast auf den gewünschten Typ sinnvoll.
Natürlich bietet das Criteria API Möglichkeiten, die Ergebnismenge einzuschränken. Dies ist mit Objekten vom Typ Criterion möglich. Criterions stellen die spätere Where-Bedingung des SQL-Statements dar. Zur Erstellung von Criterion-Objekten wird die Klasse Restrictions verwendet, mit der sich viele Einschränkungen, die auch aus HQL bekannt sind, abbilden lassen.
| ||
| ||
| ||
| ||
| ||
| ||
|
Abbildung 2 zeigt die Einschränkung mit Hilfe der Methode Restrictions.like. Mit ignoreCase wird signalisiert, dass Groß- und Kleinschreibung bei dem String-Vergleich keine Rolle spielt. Über uniqueResult wird die Ergebnismenge auf ein einziges Objekt beschränkt. Sollten mehrere Objekte zurückgeliefert werden, wird eine Exception gemeldet.
Schließlich wird ein Cast auf die Klasse Worker durchgeführt. Der Typ des zurückgelieferten Objekts ist bekannt, daher ist dieser Cast ohne Probleme möglich.
Ein Blick in die API-Dokumentation der Klasse Restrictions verrät die vielfältigen Einschränkungsmöglichkeiten, die auch vom Standard SQL bekannt sind. So sind z. B. folgende Abfragen möglich:
Einschränkungen der Restrictions-Klasse können auch logisch miteinander verknüpft werden. Dies wird durch eine entsprechende Schachtelung der Criterions erreicht. Abbildung 3 zeigt eine solche Verschachtelung. Sie besagt, dass der Mitarbeiter entweder den Vornamen "Garvin" und Nachnamen mit "Kin" beginnend aufweisen muss oder ein Gehalt von über 2000 bekommen soll.
Das Criteria API ermöglicht auch eine Gruppierung von Ergebnismengen. In Abbildung 4 wird das maximale Gehalt aller Mitarbeiter ermittelt. Mit Hilfe der Methode setProjection wird erreicht, dass nur ein einfacher Wert (in diesem Beispiel ein Float) und nicht die gesamte Entity Bean zurückgeliefert wird. Eine Einschränkung der Gruppen über eine Having-Klausel ist mit dem Criteria API nicht möglich.
Sind bei einer Abfrage Spalten verschiedener Tabellen zu berücksichtigen, wird ein Join benötigt. Abbildung 5 zeigt einen Join (genauer gesagt: einen Inner-Join) über die Tabellen "worker" und "department". Der Join wird hier deshalb verwendet, weil die Einschränkung auf den Abteilungsnamen den Zugriff auf die 2. Tabelle "department" nötig macht. Dargestellt ist, dass ein Join über einen erneuten Aufruf von createCriteria erreicht wird. Diesem 2. Aufruf wird als Argument "department" mitgegeben, welches dem Attributnamen "department" aus der Worker-Klasse entspricht. Abbildung 6 verdeutlicht die Beziehung mit Hilfe der dazugehörigen Annotation. Zu beachten ist hierbei, dass keine impliziten Joins über die Punktnotation wie bei der HQL unterstützt werden. Daher ist es nicht möglich, über den Befehl in Abbildung 7 auf den Abteilungsnamen zuzugreifen. Dieses Vorgehen führt zu einer QueryException.
Hibernate hat als Standardeinstellung lazyloading aktiviert. Das bedeutet, dass abhängige Objekte erst beim Zugriff auf deren Attribute aus der Datenbank nachgeladen werden. Es kann jedoch wünschenswert sein, abhängige Objekte direkt mitzuselektieren. Wenn z. B. klar ist, dass sowohl die Mitarbeiter als auch deren Abteilungen benötigt werden, können die Abteilungen sofort mitselektiert werden. Dies geschieht über den FetchMode, wie in Abbildung 8 dargestellt.
In diesem Zusammenhang möchten wir auch auf die Hibernate Tools [1] verweisen. Sie beinhalten u. a. ein Plugin für Eclipse, mit dessen Hilfe HQL- und Criteria-Abfragen direkt ausgeführt werden können. Das Plugin ist zur Zeit zwar noch in der Beta-Phase, es eignet sich aber sehr gut zum schnellen Ausprobieren von Criteria-Abfragen. Da Criteria-Abfragen recht komplex werden können, kann man sich auf diese Weise schnell an das gewünschte Ergebnis herantasten.
Die Möglichkeiten des Hibernate Criteria API sind sehr umfangreich und mächtig. Hat man sich erst an die Schreibweise, insbesondere bei der Verwendung von Assoziationen, gewöhnt, ist das Zusammensetzen dynamischer Abfragen keine große Herausforderung mehr. Zwar gibt es auch bei dem Criteria API Grenzen, im Notfall lassen sich aber auch direkt SQL-Befehle absetzen. Zu beachten ist allerdings auch, dass das Criteria API erst nach der HQL entwickelt wurde und somit in früheren Hibernate Versionen (2.x) nicht ganz fehlerfrei ist.
Jens Stahl (info@ordix.de).