jpa mit en

265

Upload: marcos-estrella-cardenas

Post on 03-Jul-2015

843 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Jpa Mit en
Page 2: Jpa Mit en

Daniel Röder

JPA mit Hibernate

Page 3: Jpa Mit en

Daniel Röder

JPA mit HibernateJava Persistence API in der Praxis

Page 4: Jpa Mit en

Daniel Röder: JPA mit HibernateJava Persistence API in der PraxisISBN: 978-3-86802-240-7

© 2010 entwickler.pressEin Imprint der Software & Support Verlag GmbH

Bibliografische Information der Deutschen Nationalbibliothek Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.

Ihr Kontakt zum Verlag und Lektorat:Software & Support Verlag GmbHentwickler.pressGeleitsstraße 1460599 Frankfurt am MainTel: +49(0) 69 63 00 89 - 0Fax: +49(0) 69 63 00 89 - [email protected]://www.entwickler-press.de

Lektorat: Sebastian BurkartKorrektorat: Frauke PeschLayout: SatzWERK, Siegen (www.satz-werk.com)Umschlaggestaltung: Maria RudiBelichtung, Druck & Bindung: M.P. Media-Print Informationstechnologie GmbH, Paderborn

Alle Rechte, auch für Übersetzungen, sind vorbehalten. Reproduktion jeglicher Art (Fotokopie, Nachdruck, Mikrofilm, Erfassung auf elektronischen Datenträgern oder andere Verfahren) nur mit schriftlicher Genehmigung des Verlags. Jegliche Haftung für die Richtigkeit des gesamten Werks kann, trotz sorgfältiger Prüfung durch Autor und Verlag, nicht übernommen werden. Die im Buch genannten Produkte, Warenzeichen und Firmennamen sind in der Regel durch deren Inhaber geschützt.

Page 5: Jpa Mit en
Page 6: Jpa Mit en

JPA mit Hibernate 5

Inhaltsverzeichnis

V Vorwort 15

V.1 Aufbau des Buches 15

V.2 Webseite zum Buch 16

V.3 Danksagung 16

1 Einleitung 17

1.1 Impedance Mismatch 171.1.1 Granularität 171.1.2 Vererbung 181.1.3 Objektidentität 181.1.4 Beziehungen 181.1.5 Graphennavigation 19

1.2 Hibernate 19

1.3 Java Persistence API 20

2 Hibernate, Java und das Java Persistence API – Ein Überblick 21

2.1 Java 5 und höher 212.1.1 Annotations 212.1.2 Generics 23

2.2 Das Java Persistence API 272.2.1 Entities 272.2.2 Entity Manager und Persistenzkontext 282.2.3 Java Persistence QL und die EJB QL 29

2.3 Hibernate 302.3.1 Architektur im Überblick 302.3.2 Schnittstellen 322.3.3 Module 33

2.4 Zusammenfassung 38

Page 7: Jpa Mit en

Inhaltsverzeichnis

6

3 Einführung in Hibernate und JPA 39

3.1 Beispielprojekt 393.1.1 Die Anwendungsfälle 393.1.2 Das Klassendiagramm 403.1.3 Projekt einrichten 413.1.4 Testen 44

3.2 Klassisches Hibernate 443.2.1 Hibernate-Konfiguration 453.2.2 Entity „User“ 463.2.3 HibernateUtil 483.2.4 Data Access Object 493.2.5 Testen des DAOs 53

3.3 Hibernate mit Annotations 543.3.1 Hibernate-Konfiguration 543.3.2 Entity „User“ 543.3.3 HibernateUtil 553.3.4 Data Access Object 553.3.5 Testen des DAOs 55

3.4 Hibernate als JPA Persistence Provider 563.4.1 Konfiguration des Persistence Providers 563.4.2 Die Entity „User“ 573.4.3 HibernateUtil 573.4.4 Data Access Object 583.4.5 Testen des DAOs 60

3.5 Hibernate als Persistenzschicht im Application Server 603.5.1 Konfiguration des Persistence Providers 603.5.2 Die Entity „User“ 613.5.3 HibernateUtil 613.5.4 SessionBean als Data Access Object 613.5.5 Testen der SessionBean mit Web-Service-Schnittstelle 63

3.6 Zusammenfassung 63

4 Der Aufbau und das Mapping von Entities 65

4.1 Anforderungen an eine Entity 654.1.1 Definition der Tabellen- und Spaltennamen 674.1.2 Erweiterungen der Entity mit Hibernate 68

Page 8: Jpa Mit en

Inhaltsverzeichnis

JPA mit Hibernate 7

4.2 Primärschlüssel 704.2.1 Anforderungen an den Primärschlüssel 704.2.2 Datenbankidentität, Objektidentität und -gleichheit 714.2.3 Generatoren für den Primärschlüssel 72

4.3 Komponenten 74

4.4 Assoziationen 784.4.1 1-zu-1-Beziehungen 784.4.2 1-zu-n- und n-zu-1-Beziehungen 834.4.3 N-zu-m-Beziehungen 874.4.4 Transitive Persistenz 88

4.5 Vererbung 924.5.1 SINGLE_TABLE 934.5.2 TABLE_PER_CLASS 944.5.3 JOINED 96

4.6 Collections 974.6.1 Persistente Collections 974.6.2 Collections mit Index oder Schlüssel 1004.6.3 Sortierte Collections 104

4.7 Enumerations 106

4.8 Zusammenfassung 107

5 Lebenszyklus einer Entity 109

5.1 Die Zustände einer Entity 1095.1.1 Transient 1095.1.2 Persistent 1105.1.3 Detached 110

5.2 Zustandsänderungen einer Entity 1115.2.1 Allgemeines zum Synchronisieren von Entities 1115.2.2 Methoden des EntityManagers 1115.2.3 Besonderheiten der Hibernate Session 114

5.3 Verwendung von Detached Entities 115

5.4 Callback-Methoden und Entity Listener 1185.4.1 Beschreibung der Callback-Methoden 1185.4.2 Verwendung von Entity-Listener-Klassen 1225.4.3 Default Entity Listener 1235.4.4 Ausführungsreihenfolge gleicher Callback-Methoden 124

5.5 Zusammenfassung 125

Page 9: Jpa Mit en

Inhaltsverzeichnis

8

6 Transaktionen, EntityManager und Persistenzkontext 127

6.1 Transaktionen 1276.1.1 Was ist eine Transaktion? 1276.1.2 Isolationsebenen 1286.1.3 Transaktionssteuerung in JPA 129

6.2 Locking 1306.2.1 Optimistisches Locking 1336.2.2 Lock-Modi von JPA und Hibernate 1366.2.3 Erweiterte Einstellungen für das Locking in Hibernate 138

6.3 Entity Manager und Persistenzkontext 1396.3.1 Arten und Lebenszyklus des Persistenzkontexts 1396.3.2 Erzeugen eines EntityManagers 140

6.4 Patterns für EntityManager und Hibernate Session 1436.4.1 „EntityManger/Session per Request“-Pattern 1436.4.2 „EntityManager/Session per Conversation“-Pattern 1456.4.3 „EntityManager/Session per Operation“- und

„EntityManager/Session per Application“-Antipattern 147

6.5 Zusammenfassung 148

7 Datenbankabfragen mit JPA und Hibernate 149

7.1 Das Query Interface 1497.1.1 Ausführung der Abfragen 1497.1.2 Parameter Binding 1527.1.3 Definition von benannten Abfragen in den Metadaten 153

7.2 Die Java Persistence Query Language 1547.2.1 Allgemeines 1547.2.2 Übersicht der Beispieldaten 1547.2.3 Grundaufbau der Abfragen 1557.2.4 Einschränkung der Ergebnismenge mit „where“ 1567.2.5 Sortierung mit „order by“ 1627.2.6 Joins 1637.2.7 Die „select“-Anweisung im Detail 1657.2.8 Aggregatfunktionen 1667.2.9 Die „group by“-Anweisung 1677.2.10 Polymorphe Abfragen 1687.2.11 Subqueries 1697.2.12 Massen-Update und -Delete 169

Page 10: Jpa Mit en

Inhaltsverzeichnis

JPA mit Hibernate 9

7.3 Native SQL 169

7.4 Criteria API in Hibernate 1727.4.1 Ausführung der Abfragen 1727.4.2 Einschränkung der Ergebnismenge mit Restrictions 1737.4.3 Sortierung mit org.hibernate.criterion.Order 1767.4.4 Assoziationen 1777.4.5 Abfragen mit org.hibernate.criterion.Example 1787.4.6 Die Klasse org.hibernate.criterion.DetachedCriteria 178

7.5 Hibernate-Filter 179

7.6 Criteria API und Metamodell in JPA 2.0 1807.6.1 Das statische Metamodell 1807.6.2 Das dynamische Metamodell 1827.6.3 Das Criteria API 183

7.7 Zusammenfassung 185

8 Fetching-Strategien und Caches 187

8.1 Fetching-Strategien 1878.1.1 Fetch Joins 1908.1.2 Batch Fetching mit Hibernate 1928.1.3 Subselect-Fetching mit Hibernate 194

8.2 Hibernate Query und Second Level Cache 1958.2.1 Strategien und Konfiguration 1978.2.2 Second Level Cache Provider 198

8.3 Zusammenfassung 198

9 Hibernate Types 199

9.1 Hibernate Mapping Types 199

9.2 Benutzerdefinierte Mapping Types 201

9.3 Zusammenfassung 208

A Referenz der Annotationen 209

A.1 Metadata-Annotationen 209A.1.1 Entity 209

Page 11: Jpa Mit en

Inhaltsverzeichnis

10

A.2 Callback-Annotationen 210A.2.1 EntityListeners 210A.2.2 ExcludeSuperclassListeners 210A.2.3 ExcludeDefaultListeners 210A.2.4 PrePersist 211A.2.5 PostPersist 211A.2.6 PreRemove 211A.2.7 PostRemove 212A.2.8 PreUpdate 212A.2.9 PostUpdate 212A.2.10 PostLoad 212

A.3 Annotationen für Datenbankabfragen 213A.3.1 NamedQuery 213A.3.2 QueryHint 213A.3.3 NamedQueries 213A.3.4 NamedNativeQuery 214A.3.5 NamedNativeQueries 214

A.4 Abbilden der SQL-Abfrageergebnisse 215A.4.1 SQLResultSetMapping 215A.4.2 SQLResultSetMappings 215A.4.3 EntityResult 216A.4.4 FieldResult 216A.4.5 ColumnResult 216

A.5 Referenzen auf den EntityManager und die EntityManagerFactory 217A.5.1 PersistenceContext 217A.5.2 PersistenceProperty 217A.5.3 PersistenceContexts 218A.5.4 PersistenceUnit 218A.5.5 PersistenceUnits 218

A.6 Annotationen für die Definition der Abbildungen der Entitäten 219A.6.1 Table 219A.6.2 UniqueConstraint 219A.6.3 SecondaryTable 220A.6.4 SecondaryTables 220A.6.5 CollectionTable 221

Page 12: Jpa Mit en

Inhaltsverzeichnis

JPA mit Hibernate 11

A.7 Definieren von Primärschlüsseln 222A.7.1 Id 222A.7.2 GeneratedValue 222A.7.3 EmbeddedId 222A.7.4 IdClass 223A.7.5 SequenceGenerator 223A.7.6 TableGenerator 224A.7.7 MapsId 224

A.8 Annotationen zum Überschreiben bestehender Abbildungen 225A.8.1 AttributeOverride 225A.8.2 AttributeOverrides 225A.8.3 AssociationOverride 226A.8.4 AssociationOverrides 226

A.9 Annotationen für Entitätseigenschaften 227A.9.1 Transient 227A.9.2 Column 227A.9.3 Basic 228A.9.4 Lob 228A.9.5 Temporal 229A.9.6 Enumerated 229A.9.7 Version 229A.9.8 Access 230A.9.9 Cacheable 230

A.10 Annotationen für Assoziationen 231A.10.1 JoinColumn 231A.10.2 JoinColumns 231A.10.3 ManyToOne 232A.10.4 OneToOne 232A.10.5 OneToMany 233A.10.6 ManyToMany 233A.10.7 JoinTable 234A.10.8 MapKey 234A.10.9 MapKeyClass 235A.10.10 MapKeyColumn 235A.10.11 MapKeyEnumerated 236A.10.12 MapKeyJoinColumn 236A.10.13 MapKeyJoinColumns 237A.10.14 MapKeyTemporal 237A.10.15 OrderBy 237

Page 13: Jpa Mit en

Inhaltsverzeichnis

12

A.10.16 OrderColumn 238A.10.17 PrimaryKeyJoinColumn 238A.10.18 PrimaryKeyJoinColumns 239A.10.19 ElementCollection 239

A.11 Annotationen für Vererbung 240A.11.1 Inheritance 240A.11.2 DiscriminatorColumn 240A.11.3 DiscriminatorValue 241A.11.4 MappedSuperclass 241

A.12 Annotationen für eingebettete Komponenten 242A.12.1 Embeddable 242A.12.2 Embedded 242

A.13 Hibernate-spezifische Annotationen 243A.13.1 Entity 243A.13.2 Table 244A.13.3 Index 244A.13.4 Tables 244A.13.5 Proxy 245A.13.6 AccessType 245A.13.7 BatchSize 245A.13.8 Cache 246A.13.9 Cascade 246A.13.10 Check 247A.13.11 CollectionOfElements 247A.13.12 Columns 247A.13.13 DiscriminatorFormula 248A.13.14 Fetch 248A.13.15 Filter 248A.13.16 Filters 249A.13.17 FilterDef 249A.13.18 ParamDef 249A.13.19 FilterDefs 250A.13.20 Formula 250A.13.21 Generated 250A.13.22 GenericGenerator 251A.13.23 Parameter 251A.13.24 IndexColumn 251A.13.25 LazyCollection 252A.13.26 LazyToOne 252

Page 14: Jpa Mit en

Inhaltsverzeichnis

JPA mit Hibernate 13

A.13.27 MapKey 252A.13.28 MapKeyManyToMany 253A.13.29 NamedNativeQuery 253A.13.30 NamedNativeQueries 254A.13.31 NamedQuery 254A.13.32 NamedQueries 255A.13.33 NotFound 255A.13.34 OnDelete 255A.13.35 OrderBy 256A.13.36 Parent 256A.13.37 Sort 256A.13.38 Type 257A.13.39 TypeDef 257A.13.40 TypeDefs 257A.13.41 Where 258

B Literaturverzeichnis 259

Stichwortverzeichnis 261

Page 15: Jpa Mit en
Page 16: Jpa Mit en

JPA mit Hibernate 15

VVorwort

In jedem Softwareprojekt muss man sich über die Speicherung der Daten Gedanken machen. Für Konfigurationen und wenige Daten reicht es meistens aus, diese in einfa-chen Property oder XML-Dateien abzulegen. Jedoch kommt man bei größeren Daten-mengen meistens nicht an der Verwendung einer Datenbank vorbei. Mit Java gibt es ver-schiedene Ansätze Daten in einer Datenbank abzulegen. Dabei ist die Verwendung von JDBC1 die Basis von vielen Möglicheiten, da damit SQL Statements direkt an die Daten-bank geschickt werden können. Um aber nicht in jedem Projekt das Rad bzw. die Persis-tenz der Daten neu erfinden zu müssen, gibt es zahlreiche Frameworks, wie bspw. Hiber-nate, die die typischen Funktionen in der Verwendung von JDBC kapseln. Da aber jedes Framework verschieden ist, wurde mit der Java Persistence API (JPA) eine einheitliche Schnittstelle (API) für die Persistenz in Java spezifiziert, die mittlerweile von zahlreichen Frameworks unterstützt wird. Das Buch zeigt anhand von vielen Beispielen die Möglich-keiten und die Verwendung der Java Persistence API. Dabei wird mit Hibernate auf eine solide Implementierung der JPA gesetzt.

V.1 Aufbau des BuchesIn Kapitel 1 werden zunächst die Schwierigkeiten beim Speichern von Objekten in rela-tionalen Datenbanken beleuchtet und erklärt, welche Herausforderungen sich dadurch ergeben. Des weiteren werden die „Protagonisten“ des Buches, JPA und Hibernate, vor-gestellt.

Kapitel 2 gibt einen tieferen Überblick über die Java Persistence API und deren Begriff-lichkeiten sowie die Hibernate Projekte. Außerdem werden Annotations und Generics vorgestellt, deren Kenntnis für die Verwendung der JPA unerlässlich ist. In Kapitel 3 wird das im Buch durchgängig verwendete Beispiel vorgestellt. Dabei wird besonders auf die möglichen Anwendungsszenarien von Hibernate eingegangen.

Kapitel 4 befasst sich mit dem grundlegenden Aufbau von Entites. Dabei wird auch die Verwendung von Komponenten, Assoziationen, Collections und Vererbung diskutiert. Der Lebenszyklus der Entities findet in Kapitel 5 Beachtung. Der EntityManager und die Möglichkeiten zur Transaktionssteuerung und für das Locking werden in Kapitel 6beleuchtet.

In Kapitel 7 wird der Frage nachgegangen wie gespeicherte Entites in der Datenbank gesucht und gefunden werden können. Zu diesem Zweck werden die Java Persistence Query Language (JPQL) und die Criteria APIs von JPA und Hibernate vorgestellt.

1. JDBC Überblick, http://java.sun.com/products/jdbc/overview.html

Page 17: Jpa Mit en

V – Vorwort

16

Die Strategien für das Fetching und Caching von Entites werden in Kapitel 8 erläutert. In Kapitel 9 kommen zum Schluss die Hibernate Custom Types zum Zug.

Im Anhang befindet sich eine Auflistung der Annotationen zur Angabe der Metadaten in JPA und Hibernate.

V.2 Webseite zum BuchAuf der Webseite http://www.entwickler-press.de/jpa befindet sich der gesamte Quellcode, der in den einzelnen Kapiteln verwendet wurde.

V.3 DanksagungIch bedanke mich bei Markus Kehle und Robert Hien, die mit dem Buch „Hibernate und die Java Persistence API“ die Grundlage für mein Buch lieferten. Sie gaben mir die Chance aus dem Ihren das Meinige zu machen. Weiterhin möchte ich mich bei Erik Bens bedanken, der mir durch sein Review wertvolle und hilfreiche Tipps gegeben hat. Mein Dank gilt auch meinem Arbeitgeber der Saxonia Systems AG, der mir den Rahmen für die Arbeit an dem Buch zur Verfügung stellte. Vielen Dank auch an die Lektoren Christi-ane Auf, Sandra Michel, Maike Möws und Sebastian Burkart, die mir während der lan-gen Arbeitsphase mit viel Geduld sowie Rat und Tat zur Seite standen. Schließlich möchte ich mich noch bei meiner Frau Nadine für ihre Rücksicht und Unterstützung während der langen Entstehungszeit des Buches bedanken.

Page 18: Jpa Mit en

JPA mit Hibernate 17

1 Einleitung

In diesem einleitenden Kapitel soll auf die Herausforderungen eingegangen werden, die bei der Speicherung von Objekten in relationalen Datenbanken entstehen können. Außerdem wird die Java Persistence API und Hibernate kurz vorgestellt.

1.1 Impedance MismatchIn der Softwareentwicklung ist mit Impedance Mismatch der Unterschied in der Struktur zwischen normalisierten relationalen Datenbanken und objektorientierten Klassenhie-rarchien gemeint. Die Unterschiede liegen in der Granularität, in der Vererbung, bei der Objektidentität, in den Beziehungen und in der Graphennavigation. Diese Unterschiede werden in den nächsten Abschnitten erläutert.

Relationale Datenbanken repräsentieren Daten in zweidimensionalen Tabellen. Ein Ein-trag in einer Tabelle hat einen Primärschlüssel, mit dem der Eintrag eindeutig identifi-ziert werden kann. Weiterhin gibt es Fremdschlüssel, die Tabellen miteinander in Bezie-hung bringen, indem sie auf Primärschlüssel einer anderen Tabelle zeigen.

1.1.1 Granularität

Ein objektorientiertes Modell ist typischerweise sehr feingranular, so kann es beispiels-weise eine Entity Person geben, die eine Entity Adresse als Attribut hat (siehe Abbildung 1.1).

Abbildung 1.1: Klasse Person hat eine Adresse

Objekte können jegliche Granularität haben, Tabellen hingegen sind bezüglich der Granularität beschränkt. In einer relationalen Datenbank sind die Daten der Person inklusive den Adressdaten normalerweise in einer Tabelle abgelegt (siehe Tabelle 1.1).

ID Vorname Nachname ... Adresse_PLZ Adresse_Stadt ...

1 Max Mustermann ... 01234 Musterstadt ...

2 ... ... ... ... ... ...

Tabelle 1.1: Rationale Datenbanktabellen mit Person- und Adressdaten

Person

Adresse

Page 19: Jpa Mit en

1 – Einleitung

18

Mit welchen Mitteln man ein feingranulares Objektmodell in zweidimensionalen Tabel-len abbilden kann, wird in Kapitel 4.3 gezeigt.

1.1.2 Vererbung

Vererbung ist in Programmiersprachen wie Java selbstverständlich. Relationale Daten-banken kennen aber keine Vererbung. In Kapitel 4.5 wird gezeigt, welche Strategien es zur Abbildung von Vererbungshierarchien gibt und welche Vor- und Nachteile die jeweilige mit sich bringt.

1.1.3 Objektidentität

In Java sind zwei Objekte identisch, wenn beide dasselbe Objekt sind. Wenn zwei Objekte identische Werte enthalten, dann sind die Objekte gleich, aber nicht unbedingt identisch. Objektidentität wird in Java mit dem == Operator überprüft, Objektgleichheit mit equals().

Objektidentität in Java:

Objektgleichheit in Java:

In relationalen Datenbanken wird ein Eintrag in einer Tabelle über die Daten, die er ent-hält, identifiziert; damit können gleiche Datensätze gefunden werden. Allerdings kann man nicht sicherstellen, dass diese identisch sind. Um nun für ein Objekt den entspre-chenden identischen Eintrag in der Datenbank zu finden, muss ein eindeutiger Primär-schlüssel eingeführt werden. In den Objekten wird dieser Primärschlüssel ebenso einge-fügt und somit kann über diesen die Identität zwischen Objekt und Eintrag in der Datenbank gewährleistet werden.

1.1.4 Beziehungen

Beziehungen gibt es auch in relationalen Datenbanken. Mit einem Fremdschlüssel in der einen Tabelle wird ein Primärschlüssel in einer anderen Tabelle referenziert und somit die Tabellen in Beziehung gebracht. In der objektorientierten Welt gibt es mehrere Arten von Beziehungen:

� 1-zu-1-,

� 1-zu-viele-,

� viele-zu-1- und

� viele-zu-viele-Beziehungen.

objektA == objektB;

objektA.equals(objektB);

Page 20: Jpa Mit en

Hibernate

JPA mit Hibernate 19

Diese können letztendlich alle mit Fremdschlüsseln abgebildet werden. Etwas kompli-zierter ist die 1-zu-viele-Beziehung, da dort ein Primärschlüssel einen Fremdschlüssel referenziert und bei der viele-zu-viele-Beziehung muss eine Beziehungstabelle (Join-Tabelle) eingeführt werden. Die Beziehungstabelle enthält zwei Fremdschlüssel, die jeweils auf eine Seite der Beziehung zeigen. In der viele-zu-viele-Beziehung in Abbildung 1.2 kann ein Student mehrere Dozenten (oder Professoren) haben und ein Dozent kann ebenso mehrere Studenten haben. Beziehungen werden in Kapitel 4.4 behandelt.

Abbildung 1.2: Viele-zu-viele-Beziehung in einer relationalen Datenbank

1.1.5 Graphennavigation

Über Objekte mit Java zu navigieren ist sehr leicht. Wenn beispielsweise auf alle Vor-lesungen eines Dozenten zugriffen werden soll, so wird einfach

aufgerufen. Zur Datenbank können bis dahin bereits zwei Zugriffe erfolgt sein. Einer für die Abfrage des Dozenten und ein weiterer für die Vorlesungen des Dozenten. Als Alter-native kann ein SQL-Join verwendet werden:

Damit reduziert sich die Anzahl der Datenbankzugriffe auf einen. Aber wie verhält sich der objekt-relationale Mapper? Wie kann verhindert werden, dass, wenn über alle Dozenten iteriert und dabei auf die Vorlesungen zugriffen wird, nicht jedesmal eine Abfrage an die Datenbank erfolgt (N+1-Problem)? Antworten auf diese Fragen werden in Kapitel 8 gegeben.

1.2 HibernateHibernate ist ein Open-Source-Produkt und beschreibt sich auf http://www.hibernate.orgals objekt-relationaler Persistenz- und Query-Service:

Hibernate is a powerful, high performance object/relational persistence and query service. Hiber-nate lets you develop persistent classes following object-oriented idiom- including association, inheritance, polymorphism, composition, and collections. Hibernate allows you to express queries in its own portable SQL extension (HQL), as well as in native SQL, or with an object-oriented Criteria and Example API.

dozent.getVorlesungen();

select * from DOZENT left outer join VORLESUNG where ...

<<Tabelle>> Student <<PK>>-id

<<Tabelle>> Student_Dozent <<FK>>-student_id <<FK>>-dozent_id

<<Tabelle>> Dozent <<PK>>-id

Page 21: Jpa Mit en

1 – Einleitung

20

Hibernate ist eine feste Größe unter den O/R1-Mappern und war mit Gavin King (Grün-der und Entwickler von Hibernate) auch maßgeblich an der Spezifikation der ersten Ver-sion der Java Persistence API (JPA)2 beteiligt. Der Erfolg von JPA 1.0, die vollständig durch Hibernate implementiert wird, ist sicherlich auch den fundierten Grundlagen von Hibernate zu verdanken.

1.3 Java Persistence APIIm Mai 2006 wurde das Final Release der EJB-3.0-Spezifikation (JSR-220) veröffentlicht. Ziel des JSR-220 war es, Java EE zu vereinfachen. Die Java Persistence API wurde als eigenständiger Teil der EJB-3.0-Spezifikation entwickelt und löste die Entity Beans ab.

Die wichtigsten Eigenschaften von JPA sind:

� die Entities sind einfache POJOs (Plain Old Java Objects)

� objektorientierte Klassenhierarchien mit Vererbung, Assoziationen, Polymorphismus usw. werden unterstützt

� die objektorientierte Abfragesprache Jave Persistence Query Language (JPQL)

� die API ist nicht nur in Java Enterprise Umgebungen (Applikationserver) einsetzbar, sondern auch in normalen Java Standard Umgebungen lauffähig

Die Java Persistence API 2.0 (JSR-317) wurde im Dezember 2009 als Final Release veröf-fentlicht. Im Verlauf des Buches werden die Neuerungen an den entsprechenden Stellen vorgestellt. Hier nur die wesentlichsten Punkte in Kürze:

� Collections von Basistypen

� eine Criteria API mit Metamodell

� ein Cache Interface

� Erweiterungen der JPQL Query API

� Unterstützung der Bean Validation API3

1. Abkürzung für objekt-relational.2. JSR-220 (Java Specifcation Request), http://jcp.org/en/jsr/detail?id=2203. Bean Validation API, http://jcp.org/en/jsr/summary?id=303

Page 22: Jpa Mit en

JPA mit Hibernate 21

2 Hibernate, Java und das Java Persistence API – Ein Überblick

2.1 Java 5 und höherMit Java 5 sind eine Reihe von hilfreichen und wichtigen Neuerungen in den Java-Stan-dard aufgenommen worden. In den beiden folgenden Abschnitten werden die zwei für Hibernate und JPA wichtigsten näher vorgestellt.

2.1.1 Annotations

Die in Java 5 neu hinzugekommenen Annotations bieten die Möglichkeit, Metadaten direkt im Sourcecode zu hinterlegen. Die Notwendigkeit für zusätzliche Dateien zur Speicherung der Metadaten wie z. B. XML- oder Property-Dateien entfällt somit. Die per Annotation hinterlegten Metadaten können unter anderem von Codegenerierungstools ausgelesen oder zur Laufzeit per Reflection abgefragt werden. Für den Entwickler haben Annotations den Vorteil, dass alle Informationen zentral im Sourcecode abgelegt sind und deren Zuordnung zum Sourcecode sofort ersichtlich ist.

In JPA und Hibernate können Metadaten, z. B. Mapping-Informationen, in Annotations angegeben werden. Die Verwendung von externen XML-Dateien oder von Javadoc in Verbindung mit XDoclet ist somit nicht mehr notwendig. Eine Übersicht der verfügbaren Annotations von JPA und Hibernate befindet sich im Anhang.

Verwendung von Annotations

Annotations werden direkt im Sourcecode vor dem zu markierenden Element eingefügt. Zur Kennzeichnung muss jeder Annotation ein @ vorangestellt werden. Zwischen dem @-Zeichen und dem Namen der Annotation sind Leerzeichen erlaubt. Parameter einer Annotation werden, wie bei Methoden auch, in Klammern an den Annotation-Namen angehängt.

Annotation-Parameter werden als Name-Wert-Paar angegeben, wobei die Reihenfolge der Parameter keine Rolle spielt (z. B. @MyAnnotation(para1=“hello“, para2=“world“) ). Hat die Annotation nur einen Parameter, kann der Name weggelassen werden. Bei parame-terlosen Annotations ist die Angabe der Klammern optional.

Annotations können sich auf alle wesentlichen Java-Elemente, wie Packages, Klassen, Interfaces, Enumerations, Methoden, Variablen und Methodenparameter, beziehen.

Page 23: Jpa Mit en

2 – Hibernate, Java und das Java Persistence API – Ein Überblick

22

Annotations in der Java SE 5.0

In der Java Standard Edition 5.0 wurden bereits sieben Annotations definiert. Dabei wird zwischen Standard-Annotation und Meta-Annotation unterschieden. Die Standard-Annotations sind zur normalen Verwendung beim Programmieren vorgesehen, wäh-rend die Meta-Annotations zur Definition neuer Annotation-Typen verwendet werden können.

Folgende Annotations stehen standardmäßig zur Verfügung:

� @Deprecated: Mittels dieser Annotation kennzeichnet man Methoden und Klassen, die nicht mehr verwendet werden sollen. Sie ist eine Alternative zur bisherigen Verfah-rensweise, veraltete Elemente über Javadoc-Kommentare zu markieren.

� @Override: Diese Annotation wird zur Markierung einer Methode verwendet. Der Com-piler stellt dann sicher, dass eine Methode einer Basisklasse überschrieben wird. Andern-falls wird eine Compiler-Fehlermeldung ausgegeben. Dadurch wird ein Überschreiben sichergestellt und Fehler aufgrund falsch geschriebener Methoden werden vermieden.

� @SuppressWarnings: Dient zur Unterdrückung von Compiler-Warnungen. Die War-nungen müssen als Parameter angegeben werden. Es werden alle Meldungen unter-drückt, die sich auf Elemente beziehen, die durch das markierte Element (z. B. eine Methode) eingeschlossen werden.

Listing 2.1 zeigt die Klasse AnnotationsHelloWorld. Die Methode sayHello() ist als veraltet markiert und löst bei Verwendung eine Compiler-Warnung aus. Die Markierung von toString() mit @Override stellt sicher, dass die Methode auch wirklich toString() aus Object überschreibt.

Als Meta-Annotations stehen folgende Elemente zur Verfügung:

� @Documented: Markierte Annotations werden automatisch bei der Verwendung von Javadoc, zur Erzeugung der Dokumentation, berücksichtigt.

public class AnnotationsHelloWorld {

@Deprecated public String sayHello() { return "Hello World"; }

@Override public String toString() { return "Hello World"; }}

Listing 2.1: Verwendung von Annotations

Page 24: Jpa Mit en

Java 5 und höher

JPA mit Hibernate 23

� @Inherited: Der Annotation-Typ wird automatisch vererbt und gilt automatisch auch für das entsprechende Element in allen abgeleiteten Subklassen.

� @Retention: Gibt an, wie lange die Annotation verfügbar ist. Es stehen folgende Werte zur Verfügung: � SOURCE: Die Informationen stehen nur bis zur Compile-Zeit zur Verfügung und

werden dann vom Compiler entfernt.� CLASS: Die Metadaten werden in den Class-Dateien abgespeichert, aber nicht

von der VM geladen. � RUNTIME: Annotations werden in der Class-Datei abgelegt und von der VM

geladen und stehen somit zur Auswertung per Reflection zur Verfügung.

� @Target: Mit Target wird definiert, welchen Elementen (Klasse, Methode, Parameter etc.) eine Annotation zugeordnet werden kann.

Eigene Annotations definieren

Listing 2.2 zeigt die Definition eines eigenen Annotation-Typs. Die Annotation MyAnnota-tion enthält drei Parameter: param1, param2 und counter. Für counter wurde ein Default-Wert von 0 definiert. Eine Angabe dieses Parameters bei Verwendung der Annotation ist somit optional.

Mit @Retention(RetentionPolicy.Source) wurde die Verfügbarkeit auf die Compile-Zeit eingeschränkt, ein Zugriff auf diese Annotation zur Laufzeit oder ein Auslesen aus der Class-Datei ist daher nicht möglich.

Durch Setzen von @Target({ElementType.METHOD, ElementType.TYPE}) wird festgelegt, dass MyAnnotation nur auf Methoden- und Klassenebene verwendet werden kann.

2.1.2 Generics

Die in der Java Standard Edition 5.0 neu eingeführten Generics erlauben die Abstraktion von Typen. Damit ist es möglich, Klassen und Methoden zu definieren, die generisch, also unabhängig von einem konkreten Typ, sind. Da JPA und Hibernate die Verwendung von Generics unterstützt, werden diese im folgenden Abschnitt näher erläutert.

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.SOURCE)public @interface MyAnnotation { String param1(); String param2(); int counter() default 0;}

Listing 2.2: Definition eines eigenen Annotation-Typs

Page 25: Jpa Mit en

2 – Hibernate, Java und das Java Persistence API – Ein Überblick

24

Verwendung von Generics

Ein gutes Beispiel ist das Collection Framework:

Bisher war der Cast auf Double in der letzten Zeile notwendig, da sonst ein Fehler durch den Compiler ausgegeben worden wäre, denn der Rückgabewert von get() ist Object.

Außerdem birgt diese Art der Verwendung von Collections das Risiko, dass ein Objekt in die Liste eingeführt wird, dessen Typ inkompatibel ist. Dies wird nicht durch den Com-piler erkannt, sondern tritt erst zur Laufzeit durch eine ClassCastException zutage.

Mithilfe der neuen generischen Collections lässt sich der obige Code folgendermaßen aus-drücken:

Mit List<Double> v wird angegeben, dass v eine Liste von Double ist. Der in spitzen Klammern angegebene Typ (hier Double) definiert den konkreten Typparameter, der für diese Liste verwendet wird. Die korrekte Verwendung dieser Liste wird durch den Com-piler sichergestellt. Daher ist das Einfügen eines unpassenden Typs nicht mehr möglich und ein Verstoß wird bereits zur Compile-Zeit erkannt. Der vorher notwendige Cast kann entfallen, denn der Rückgabewert von get() ist nun Double.

Falls mehrere Typparameter angegeben werden müssen, so werden sie durch Komma getrennt (z. B. Hashtable<Long, String>).

Die Verwendung von Generics verbessert somit die Lesbarkeit und hilft durch die erhöhte Typsicherheit, die Zuverlässigkeit des Codes zu erhöhen.

Einschränkungen und Besonderheiten

Bei der Verwendung von Generics muss man aber auch eine Reihe von Einschränkungen und Besonderheiten beachten, die unter anderem daher rühren, dass die Unterstützung von Generics nur auf Basis des Compilers umgesetzt wurde. Der VM selbst sind Gene-rics unbekannt, das heißt, die Typinformationen bei Generics stehen nur zur Compile-Zeit zur Verfügung.

Als Konsequenz hieraus liefert z. B. der Vergleich des Typs von Vector<String> und Vec-tor <Integer> true, denn zur Laufzeit gibt es nach wie vor nur den Typ Vector.

Außerdem gibt es zwischen z. B. List<Object> und List<String> keine Vererbungsbezie-hung. Das heißt, eine Liste von String ist somit keine Liste von Object.

List v = new Vector();v.add(new Double(1.0));Double d = (Double)v.get(0);

List<Double> v = new Vector<Double>();v.add(new Double(1.0));Double d = v.get(0);

Page 26: Jpa Mit en

Java 5 und höher

JPA mit Hibernate 25

Folgender Code führt zu einem Compile-Fehler:

Wildcards und Bounds

Um dieselben Möglichkeiten wie in dem bisherigen Collection-API zu haben, wie bei-spielsweise die Implementierung von Such- oder Sortieralgorithmen, die unabhängig von der verwendeten Collection sind, gibt es die so genannten Wildcards. Als Wildcard-zeichen wird ? verwendet. Ein Vector<?> steht für einen Vector mit beliebigem Inhalt. Folgender Code führt somit nicht mehr zu einem Compile-Fehler:

Allerdings gibt es auch bei der Verwendung von Wildcards einige Einschränkungen. So ist das Erzeugen einer Collection mit unbekanntem Typ genauso wenig möglich wie das Hin-zufügen eines Objekts zu einer solchen Collection, denn der Typ, für den ? steht, ist ja eben nicht bekannt.

Wildcards können darüber hinaus mit so genannten Bounds angegeben werden. Durch Angabe einer oberen Schranke mittels extends kann festgelegt werden, dass ein Typ vom angegebenen oder einem abgeleiteten Typ sein muss. Eine mittels super definierte untere Schranke legt fest, dass ein Typ vom angegebenen oder einem Supertyp sein muss.

Eigene generische Typen definieren

Generics sind nicht auf die Verwendung bestehender Klassen beschränkt. Auch eigene generische Typen können definiert werden.

Listing 2.3 zeigt die Definition der generischen Klasse MyGeneric, die zwei Typparameter (T und U) enthält. Die Typparameter können wie normale Typen bei der Definition der Klasse verwendet werden.

Vector<Object> v1 = new Vector<Object>();Vector<String> v2 = new Vector<String>();v1 = v2; //Compilefehler !

Vector<?> v1;Vector<String> v2 = new Vector<String>();v1 = v2;

List<? extends Number> l1; // Number oder Subtyp bspw. IntegerList<? super String> l2; // String oder Supertyp bspw. Object

public class MyGeneric<T, U> { private T key; private U value; public MyGeneric(T key, U value){ this.key = key; this.value = value; }

Listing 2.3: Definition einer eigenen generischen Klasse

Page 27: Jpa Mit en

2 – Hibernate, Java und das Java Persistence API – Ein Überblick

26

Eine Verwendung der Typparameter T und U in einem statischen Context ist allerdings nicht möglich. Folgende Definition innerhalb von MyGeneric würde zu einem Compile-Feh-ler führen:

Da statische Elemente nur einmal pro Klasse existieren, wäre eine typsichere Verwen-dung nicht garantiert, wenn zwei Instanzen einer generischen Klasse mit verschiedenen konkreten Typen initialisiert würden. Daher wird die Verwendung von statischen Ele-menten in Verbindung mit Typparametern durch den Compiler unterbunden.

Generische Methoden

Auch die Definition von generischen Methoden ist vorgesehen.

Listing 2.4 zeigt die Definition der generischen Methode sayHello(), die zwei Typpara-meter enthält. Im Gegensatz zu generischen Klassen muss bei der Verwendung generi-scher Methoden der Typparameter beim Aufruf nicht explizit angegeben werden, da er vom Compiler ermittelt wird.

public T getKey() { Return key; } public U getValue() { Return value; }}

private static T firstKey; // Compilefehler !

public class GenericMethod { public static <E, T> void sayHello(E pre, T post) { System.out.println(pre.toString() + " hello " + post.toString()); } public static void main(String[] args) { sayHello("generic", new StringBuffer("world")); }}

Listing 2.4: Definition und Verwendung einer generischen Methode

Listing 2.3: Definition einer eigenen generischen Klasse (Forts.)

Page 28: Jpa Mit en

Das Java Persistence API

JPA mit Hibernate 27

2.2 Das Java Persistence APIMit der EJB-3.0-Spezifikation wurde als Teil der Java Enterprise Edition 5.0 Platform ein neues Java Persistence API standardisiert. Es wurde im Rahmen des JSR 220 durch den Java Community Process von einer Expertengruppe erarbeitet, zu der neben Gavin King, dem Gründer von Hibernate, auch andere bekannte Persönlichkeiten aus der Java-Welt gehörten.

Das Ziel von JPA ist eine Standardisierung des Basis-API sowie der Metadaten eines objekt-relationalen Persistenzmechanismus für Java. Dabei handelt es sich nicht um ein fertiges Fra-mework, sondern lediglich um eine Spezifikation, die unter anderem von Hibernate imple-mentiert wird.

JPA ist nicht auf den Einsatz unter Java EE begrenzt. Im Gegensatz zu EJB 2.1 ist es auch in normalen Java-SE-Anwendungen außerhalb eines Java-EE-Containers einsetzbar. Die Spezifikation wurde durch Ideen und Zulieferungen anderer Persistenzframeworks wie Hibernate, TopLink und JDO bereichert. So ist es nicht weiter verwunderlich, dass JPA den gleichen Ansatz wie Hibernate verfolgt und die Entitäten als POJOs darstellt.

Zur Definition der Metadaten sind die in Java 5.0 neu hinzugekommenen Annotations vorgesehen, die direkt in den Entity-Klassen hinterlegt werden. Alternativ können natürlich weiterhin die bekannten XML- (Deployment-)Deskriptor-Dateien zur Angabe der Metadaten verwendet werden.

2.2.1 Entities

Die so genannten Entities sind leichtgewichtige, persistente Objekte und der zentrale Teil des JPA. Sie lösen die schwergewichtigen Entity Beans der älteren Java-EE-Versionen ab.

Für Entity-Klassen ist sowohl die Verwendung von abstrakten als auch konkreten Java-Klassen möglich. Dabei werden Vererbung, polymorphe Assoziationen und polymorphe Abfragen unterstützt.

Entity-Klassen müssen nicht von einer bestimmten Basisklasse abgeleitet sein. Somit können sie in einer eigenen Vererbungshierarchie verwendet werden. Entities und nor-male Java-Klassen können innerhalb einer Vererbungshierarchie beliebig kombiniert werden. Innerhalb der Entity-Klassen können die persistenten Attribute durch Annota-tions mit Mapping- und anderen Metadaten versehen werden.

Listing 2.5 zeigt eine einfache Entity-Klasse, in der die Mapping-Daten als Annotation definiert werden:

@Entitypublic class Book implements Serializable{ @Id private Long id; private String title; @ManyToMany private Set<Author> authors = new HashSet<Author>();

Listing 2.5: Eine einfache Entity-Klasse

Page 29: Jpa Mit en

2 – Hibernate, Java und das Java Persistence API – Ein Überblick

28

2.2.2 Entity Manager und Persistenzkontext

Mithilfe eines Entity Managers werden die Entities verwaltet. Er stellt das API für den Zugriff auf einen Persistenzkontext bereit. Als Persistenzkontext bezeichnet man eine Menge von Entity-Objekten, in der für jede Instanz einer Entität innerhalb der Daten-bank höchstens ein Java-Objekt im Kontext existiert. Einem Entity Manager ist immer genau ein Persistenzkontext zugeordnet, der von ihm verwaltet wird.

Es wird zwischen Container-managed Entity Managern und Application-managed Entity Managern unterschieden.

� Container-managed Entity Manager1 kommen innerhalb eines Java-EE-Containers zum Einsatz. Bei Container-managed Entity Managern ist der Java-EE-Container für das Erzeugen und Schließen des Entity Managers sowie des notwendigen JTA (Java-Transaction-API-)Transaktionen verantwortlich. Die Verwaltung geschieht transpa-rent für die Anwendung. Zugriff auf den Entity Manager erhält die Anwendung ent-weder über Dependency Injection oder per JNDI2. Es ist lediglich die Angabe der Annotation @PersistenceContext notwendig.

public Book(){} // Default Konstruktor

public Set<Author> getAuthors() { return authors; } public void setAuthors(Set<Author> authors) { this.authors = authors; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; }}

1. Siehe auch Kapitel 3.52. Java Naming and Directory Interface: http://java.sun.com/products/jndi/

Listing 2.5: Eine einfache Entity-Klasse (Forts.)

Page 30: Jpa Mit en

Das Java Persistence API

JPA mit Hibernate 29

� Application-managed Entity Manager3 werden, wie der Name schon andeutet, von der Anwendung selbst verwaltet. Für das Erzeugen wird eine EntityManagerFactoryverwendet. Für Transaktionen steht entweder JTA4 oder eine lokale Transaktion (z. B. JDBC-Transaktion), die über das EntityTransaction API kontrolliert wird, zur Verfügung.

Für die Gültigkeitsdauer eines Persistenzkontexts gibt es zwei Alternativen:

� transaction-scoped: Die Gültigkeitsdauer entspricht exakt der Dauer einer Transak-tion. d. h., nach Abschluss der Transaktion wird der Persistenzkontext geschlossen.

� extended: Die Gültigkeitsdauer ist unabhängig von einer Transaktion und kann meh-rere Transaktionen umfassen. Der Persistenzkontext muss manuell über den Entity Manager geschlossen werden.

2.2.3 Java Persistence QL und die EJB QL

Queries können sowohl in der aus früheren EJB-Versionen bekannten EJB Query Language (EJB QL) wie auch in SQL formuliert werden. Die EJB QL wurde dabei um eine Reihe neuer Features erweitert, wie z. B. Massen-Updates und -Deletes, Joins, Group By oder der Mög-lichkeit, Subqueries auszuführen. Die Neuerungen wurden auch durch den neuen Namen Java Persistence Query Language (JPQL) deutlich. Die Verwendung von JPQL hat gegenüber SQL den Vorteil, dass die Abfragen unabhängig vom Typ der Datenbank und somit wesent-lich portabler sind.

Eine Query-Instanz wird mit einem EntityManager erzeugt. Dafür stehen verschiedene Methoden zur Verfügung, die in Kapitel 7.1 noch näher erläutert werden.

Das Query-Interface bietet eine Reihe von Methoden zur Angabe von Parametern sowie zur Durchführung der durch das Query-Objekt repräsentierten Abfrage.

Queries können auch als so genannte „Named Queries“ statisch innerhalb der XML-Metadaten oder als Annotation definiert werden. Dies hat den Vorteil, dass alle Abfragen an einer zentralen Stelle verwaltet und gepflegt werden können. In Listing 2.6 wird die Definition der Named Query mit der Annotation @NamedQuery beispielhaft gezeigt.

Im Gegensatz zu früheren EJB-Versionen sind alle Queries polymorph, das heißt, eine Abfrage liefert nicht nur die Instanzen der Entity-Klasse, sondern auch Instanzen von abgeleiteten Entity-Klassen, wenn diese die Abfragebedingungen erfüllen.

3. Siehe auch Kapitel 3.44. Java Transaction API, erlaubt die Steuerung der Transaktionsverwaltung:

http://java.sun.com/products/jta/

@NamedQuery( name="findAllBooksWithTitle", query="SELECT b FROM Book b WHERE b.title LIKE :bookTitle")

Listing 2.6: Verwendung von Named Queries

Page 31: Jpa Mit en

2 – Hibernate, Java und das Java Persistence API – Ein Überblick

30

Queries können, wie bereits erwähnt, auch in SQL formuliert werden. Die Verwendung von SQL ist z. B. dann sinnvoll, wenn JPQL nicht ausreicht, um eine Abfrage zu formu-lieren, oder datenbankspezifische SQL-Features zum Einsatz kommen sollen. Als Konse-quenz sind die in SQL formulierten Abfragen natürlich nicht mehr datenbankunabhän-gig und müssen eventuell, im Gegensatz zu JPQL Queries, bei einem Wechsel der Datenbank angepasst werden.

Als Ergebnis einer in SQL formulierten Abfrage können sowohl skalare Werte wie auch Enti-ties oder auch eine Mischung aus beidem zurückgegeben werden. Wenn eine Abfrage ver-schiedene Entity-Klassen als Ergebnis liefert, muss ein so genanntes @SqlResultSetMappingangegeben werden, das die Java-Klassen der Entities deklariert (Listing 2.7).

2.3 Hibernate

2.3.1 Architektur im Überblick

Ein grober Überblick über die Architektur von Hibernate ist in Abbildung 2.1 dargestellt. Die Grafik zeigt, wie Hibernate mittels Konfiguration und Datenbank der Anwendung persistente Objekte zur Verfügung stellt.

Hibernate ist sehr flexibel und unterstützt unterschiedlichste Einsatzszenarien. Bei-spielsweise kann die eigentliche Anwendung die Datenbankverbindungen und die Transaktionssteuerung selbst bereitstellen, dadurch wird nur ein minimaler Teil von Hibernate genutzt. Andererseits kann dies alles von Hibernate gekapselt werden, das heißt, die Anwendung muss sich nicht mit JDBC, JTA, JNDI und weiteren für die Persis-tierung wesentlichen APIs auseinandersetzen und überlässt die Details Hibernate.

Query q = em.createNativeQuery( "SELECT b.id, b.title, b.publisher, p.id, p.name"+ "FROM Book b, Publisher p " + "WHERE (b.title LIKE 'Hibernate%') AND (b.publisher = p.id)" ,"BookPublisherResults");@SqlResultSetMapping(name="BookPublisherResults", entities={ @EntityResult(entityClass=Book.class), @EntityResult(entityClass=Publisher.class) })

Listing 2.7: Verwendung von @SqlResultSetMapping

Page 32: Jpa Mit en

Hibernate

JPA mit Hibernate 31

Abbildung 2.1: Überblick über die Architektur von Hibernate

In Abbildung 2.2 sieht man die Architektur von Hibernate im letzteren Szenario, die wei-teren Abschnitte in Kapitel 2.3.2 erläutern die in der Grafik genannten Schnittstellen. Eine weitere wesentliche Bedeutung hat Hibernate als Implementierung des JPA erhalten und kann somit als Persistence-Provider in Java-SE- und Java-EE-Umgebungen eingesetzt werden.

Abbildung 2.2: Hibernate kapselt JDBC, JTA und JNDI

Anwendung

Hibernate

Persistente Objekte

hibernate.cfg.xmlhibernate.properties

Mapping (Annotationen / XML)

Datenbank

JNDI

AnwendungTransiente

Objekte

TransactionFactory

SessionFactory

ConnectionProvider

Session

Persistente Objekte

Transaction

Datenbank

JDBC JTA

Page 33: Jpa Mit en

2 – Hibernate, Java und das Java Persistence API – Ein Überblick

32

2.3.2 Schnittstellen

SessionFactory

Die SessionFactory hält in ihrem Cache generierte SQL Statements und die Mapping-Daten. Mit der SessionFactory können für die Anwendung Session-Instanzen erzeugt werden. Typischerweise existiert in einer Anwendung nur eine SessionFactory. Sie ist sehr schwergewichtig, das heißt sehr „teuer“ in der Erstellung, und wird normalerweise während der Initialisierung der Anwendung erzeugt. Wenn jedoch mehrere Daten-banken von der Anwendung unterstützt werden müssen, wird für jede Datenbank eine SessionFactory angelegt.

Session

Die Session ist das wichtigste Interface in einer Anwendung mit Hibernate. Sie ist leicht-gewichtig und „billig“ zu erzeugen. In einer Anwendung werden sehr viele Sessions erzeugt und gelöscht, normalerweise bei jeder Anfrage oder für jeden Anwendungsfall (Use Case). Die Session kapselt eine Datenverbindung und verwaltet die Transaktion. Sie hält außerdem den First-Level-Cache und entdeckt automatisch Änderungen an Objek-ten. Es ist zu beachten, dass eine Session nicht „Thread-Safe“ ist. Das heißt, die Entities einer Session dürfen nur im Thread dieser Session verwendet werden. Die Session ist eine Implementierung des EntityManager-Interfaces von JPA. Mit der Methode getDele-gate() wird die konkrete Session vom EntityManger zurückgegeben.

Configuration

Die Configuration erlaubt der Anwendung, Konfiguration und Mappings beim Erzeugen einer SessionFactory zu spezifizieren. Eine Configuration ist nur zur Initialisierungszeit von Hibernate relevant, nach Erzeugung der SessionFactory kann keine Änderung mehr daran vorgenommen werden. Eine Beziehung zur Configuration wird nicht aufrecht erhalten.

Transaction

Die Transaction abstrahiert die Anwendung von der unterliegenden JDBC-, JTA- oder Corba-Transaktion. Eine Transaction ist ein „Single-Threaded“, kurzlebiges Objekt, das von der Anwendung benutzt wird, um atomare Abschnitte zu spezifizieren. Transaktio-nen werden in Kapitel 6.1 erläutert.

ConnectionProvider

Der optionale ConnectionProvider ist eine Factory für Datenbankverbindungen. Er abs-trahiert die Anwendung von einer Datasource oder einen DriverManager. Der Connection-Provider wird intern von Hibernate verwendet, um Datenverbindungen zu erhalten.

TransactionFactory

Eine ebenfalls optionale Factory für Instanzen von Transactions. Konkrete Implementie-rungen sind CMTTransactionFactory, JDBCTransactionFactory und JTATransactionFactory. In der Konfiguration kann die gewünschte Implementierung angeben werden.

Page 34: Jpa Mit en

Hibernate

JPA mit Hibernate 33

2.3.3 Module

Hibernate Core

Hibernate Core ist die Grundlage für alle Hibernate-Anwendungen, da es die in Kapitel 2.3.2 vorgestellten Schnittstellen zur Verfügung stellt. Daneben wird das objektrelatio-nale Mapping für Basistypen, Collections, Vererbungshierarchien und Assoziationen implementiert.

Die komplexe Abfragesprache HQL, Kriterien-Queries sowie die mit Hibernate 3.1 ein-geführten Filter werden durch Hibernate Core dem Persistenzframework hinzugefügt.

Hibernate Annotations

Das Projekt Hibernate Annotations implementiert die durch das neue JPA standardisier-ten Annotations.

In Hibernate 2.x wurden diese Metadaten entweder in zusätzlichen XML-Dateien oder in speziellen durch XDoclet ausgewerteten Javadoc-Kommentaren abgelegt. Mit den neuen Java Annotations steht nun ein standardisierter Mechanismus zur Verfügung, der durch viele IDEs unterstützt wird.

Die Verwendung der Hibernate Annotations ist jedoch optional, d. h., die Metadaten können nach wie vor auch im XML-Format oder als XDoclet-Kommentare angegeben werden. Die Hibernate Annotations fügen einfach eine weitere Möglichkeit hinzu, um für Hibernate relevante Metadaten zu deklarieren, die sich auch mit den bisherigen Lösungen mischen lässt. Es ist also für bestehende Lösungen möglich, schrittweise von XML-Metadaten zu den neuen Annotations zu migrieren.Neben den standardisierten Annotations stellt das Hibernate-Annotations-Projekt noch eine Reihe von Hibernate-spezifischen Annotations zur Verfügung, die zusammen mit den JPA Annotations einge-setzt werden können:

� Die Annotation @org.hibernate.annotations.Entity erlaubt Einstellungen, die über den Umfang der JPA-Spezifikation hinausgehen. So kann z. B. über den Parameter mutable angegeben werden, ob die Entity veränderbar ist. Mit selectBeforeUpdatekann konfiguriert werden, dass Hibernate nie ein SQL UPDATE ausführt, ohne zu überprüfen, ob sich eine Entity tatsächlich verändert hat. Der Parameter optimistic-Lock erlaubt die Festlegung der Lock-Strategie. Zu beachten ist, dass diese Annotation kein Ersatz der Annotation @javax.persistence.Entity ist, sondern zusätzlich angege-ben werden muss.

� Die Annotation @org.hibernate.annotations.GenericGenerator ermöglicht die Angabe eines Hibernate-ID-Generators. In Kombination mit der JPA-Annotation @Id lassen sich so alle in Hibernate verfügbaren ID-Generierungsmechanismen nutzen.

� Mit @org.hibernate.annotations.AccessType kann die Art, wie die Persistenz-Engine auf Attribute einer Entity zugreift, bis auf Attributebene individuell festgelegt werden. So kann z. B. ein bestimmtes Attribut definiert werden, auf das nur über die Getter- und Setter-Methoden zugegriffen wird, während auf die übrigen Attribute innerhalb derselben Entity direkt, also auf die Instanzvariablen, zugegriffen wird.

Page 35: Jpa Mit en

2 – Hibernate, Java und das Java Persistence API – Ein Überblick

34

Die hier vorgestellten Annotations sind nur ein kurzer Auszug aus den Erweiterungen, die das Hibernate-Annotations-Projekt bereithält. So sind unter anderem auch zusätz-liche Annotations für den Umgang mit Vererbung, Assoziationen, Collections und Que-ries enthalten. Eine Übersicht der Annotations von JPA und Hibernate befindet sich im Anhang.

Hibernate Validator

Neben den zahlreichen kleineren Erweiterungen für das objektrelationale Mapping ent-hält das Hibernate-Annotations-Projekt auch ein Gruppe von Annotations, die unter dem Begriff Hibernate Validator zusammengefasst werden. Mit dem JSR 3035 wurde die Validierung von Java Beans standardisiert, wobei Hibernate Validator eine entspre-chende Implementierung ist.

Der Hibernate Validator ermöglicht die Deklaration von Bedingungen, die eine Entity erfüllen muss, um gültig zu sein. Die Bedingungen, so genannte Constraints, werden als Annotations direkt in der betroffenen Entity deklariert, wobei sowohl Methoden wie auch Instanzvariablen markiert werden können. Zu jeder dieser Annotations gehört eine entsprechende Validator-Implementierung, die die tatsächliche Überprüfung ausführt.

Diese Überprüfung der Bedingungen kann an verschiedenen Stellen innerhalb einer mehrschichtigen Anwendung geschehen, z. B. in der Präsentationsschicht oder der Datenbank. Die Bedingungen werden aber immer nur an einer einzigen Stelle deklariert.

Hibernate Validator bietet die Möglichkeit, die Constraints direkt über die Entity-Objekte zu überprüfen, z. B. in Form von Entity Listenern, die vor einem Insert oder Update aufgerufen werden. Eine weitere Möglichkeit besteht darin, die Bedingungen bei der Generierung des Datenbankschemas in dieses zu integrieren, um die Überwachung der Bedingungen der Datenbank selbst zu überlassen. Der Einsatz von Hibernate Valida-tor ist dabei nicht auf den Einsatz innerhalb von Hibernate beschränkt, sondern kann auch in Projekten ohne den Hibernate O/R-Mapper verwendet werden.

Die in Hibernate Validator standardmäßig enthaltenen Constraints decken bereits ein breites Spektrum verschiedener Überprüfungen ab:

� @Length: Überprüfung von String-Längen

� @Max: Maximalwert für numerische Werte

� @Min: Minimalwert für numerische Werte

� @NotNull: Überprüfung auf NULL

� @Past: Überprüft, ob ein Datum in der Vergangenheit liegt

� @Future: Überprüft, ob ein Datum in der Zukunft liegt

� @Pattern: Überprüft, ob ein Attribut einem regulären Ausdruck entspricht

� @Range: Wertebereich für numerische Werte

� @Size: Überprüfung der Länge von Collections, Arrays und Maps

5. Bean Validation API http://jcp.org/en/jsr/summary?id=303

Page 36: Jpa Mit en

Hibernate

JPA mit Hibernate 35

� @AssertFalse: Überprüft boolean Werte auf false

� @AssertTrue: Überprüft boolean Werte auf true

� @Valid: Führt eine Überprüfung des markierten Objekts durch

Die verfügbaren Constraints für Hibernate Validator sind aber nicht auf diese standard-mäßig enthaltenen beschränkt, sondern können beliebig durch selbst definierte Con-straints erweitert werden. Die Definition eines neuen Constraints besteht dabei aus zwei Teilen: der Definition einer Annotation sowie der Implementierung einer Validator-Klasse.

In Listing 2.8 wird die Verwendung von Hibernate Validator zur Überprüfung einer Book-Entity demonstriert: Der Titel eines Buches darf maximal 100 Zeichen lang und nicht null sein.

Die tatsächliche Ausführung der Überprüfung einer Book-Entity kann mit folgendem Codefragment durchgeführt werden:

Für jede verletzte Bedingung liefert ein so genannter ClassValidator ein InvalidValue-Objekt, das Details über die nicht erfüllte Bedingung enthält.

Die ClassValidator-Instanz sollte normalerweise nur einmal pro Anwendung erzeugt und danach gecacht werden.

Hibernate EntityManager

Das Hibernate-EntityManager-Projekt implementiert das durch das JPA standardisierte API.

Wie im Standard vorgesehen, lässt sich der Hibernate EntityManager sowohl innerhalb eines Java-EE-Containers einsetzen wie auch als Standalone-Lösung außerhalb eines Containers.

Hibernate EntityManager setzt dabei auf den Hibernate Core auf und verwendet ihn als Persistenz-Engine. Die Verwendung des Hibernate EntityManagers entspricht exakt der Spezifikation.

@Entitypublic class Book implements Serializable{ @Id private Long id; @Length(max=100) @NotNull Private String title; ...}

Listing 2.8: Definition von Constraints

ClassValidator bookValidator = new ClassValidator( Book.class );InvalidValue[] validationMessages = bookValidator.getInvalidValues(book);

Page 37: Jpa Mit en

2 – Hibernate, Java und das Java Persistence API – Ein Überblick

36

Hibernate Search

In vielen Projekten ist die Suche nach vorhandenen Entities in der Datenbank eine Her-ausforderung, die viel Arbeit erfordert. Vor allem, wenn die Suchparameter durch den Benutzer angegeben werden, hat man mit Ungenauigkeiten und sogar Schreibfehlern zu rechnen.

Das Search-Projekt erweitert für diese Problematik den Funktionsumfang von Hibernate um eine Volltextsuche. Dabei wird auf Apache Lucene als mächtige Such-Engine zurück-gegriffen, die zum Verwalten der Indexeinträge und für das Ausführen der Suche ver-wendet wird.

Die Grundkonfiguration von Hibernate Search ist denkbar einfach, da lediglich der DirectoryProvider mithilfe der Property hibernate.search.default.directory_providerdefiniert werden muss. Als Implementierung des DirectoryProvider-Interface steht bspw. org.hibernate.search.store.FSDirectoryProvider zur Verfügung, der den Index im Dateisystem ablegt. Dafür muss allerdings noch die entsprechende Position mit der Pro-perty hibernate.search.default.indexBase angegeben werden.

Bei der Verwendung von Hibernate Annotations bzw. Hibernate EntityManager wird die Indizierung von Hibernate Search automatisch aktiviert. Hingegen muss bei der Zusammenarbeit mit Hibernate Core der FullTextIndexEventListener den drei Hibernate Events post-update, post-insert und post-delete bekannt gemacht werden, damit der Index aktuell bleibt. Wird Hibernate Core ab Version 3.2.6 verwendet, so sollte der Full-TextIndexEventListener ebenfalls an die Events post-collection-recreate, post-collec-tion-remove und post-collection-update gebunden werden.

Neben der grundlegenden Konfiguration sind noch einige Anpassungen in den Entities vorzunehmen, um die Volltextsuche zu verwenden. Listing 2.9 zeigt ein einfaches Beispiel.

@Entity@Indexedpublic class Book { @Id @DocumentId private Long id; @Field private String title; @Field private author; ... public Book(){} // Default Konstruktor ...}

Listing 2.9: Anpassungen an einer Entity für die Volltextsuche

Page 38: Jpa Mit en

Hibernate

JPA mit Hibernate 37

Die Annotation @org.hibernate.search.annotations.Indexed markiert die Entity als indi-zierbar. Hibernate Search benötigt einen eindeutigen Schlüssel der Entity für den Index. Die Annotation @org.hibernate.search.annotations.DocumentId wird für diesen Zweck verwendet und sicherlich in den meisten Fällen auf den Primätschlüssel der Entity ange-wandt. Schließlich werden mit der Annotation @org.hibernate.search.annotations.Fieldalle Attribute der Entity gekennzeichnet, die in die Volltextsuche mit einbezogen werden sollen.

Das Ausführen der Volltextsuche erfolgt wie in Listing 2.10 gezeigt.

Natürlich kann die Volltextsuche auch mit einer Session und der Methode search.create-FullTextSession(session) erzeugt werden.

Hibernate Tools

Die Hibernate Tools bieten Unterstützung bei der täglichen Arbeit mit Hibernate und vereinfachen den Entwicklungsprozess.

Hibernate Shards

In manchen Projekten hat man das Problem, dass mehrere relationale Datenbanken ver-wendet werden müssen. Meist sind die Gründe verschieden und reichen vom Anbinden alter Datenbestände bis hin zu mehreren Mandanten auf einem System, mit der rechtli-chen Vorgabe deren Daten explizit zu trennen. Mit Hibernate Shards ist ein relativ junges Projekt entstanden, das sich dieser Problematik widmet.

Hibernate Shards ist zurzeit noch im Betastadium, lohnt aber auf jeden Fall einen weite-ren Blick, wenn man vor dem Problem steht, mehrere relationale Datenbanken einsetzen zu müssen.

EntityManager em = entityManagerFactory.createEntityManager();FullTextEntityManager ftem = createFullTextEntityManager(em);MultiFieldQueryParser parser = new MultiFieldQueryParser( new String[]{"title", "author"}, new StandardAnalyzer());Query query = parser.parse( "Hibernate" );Org.hibernate.Query hibQuery = ftem.createFullTextQuery( query, Book.class );List result = hibQuery.list();

Listing 2.10: Beispiel einer Volltextsuche

Page 39: Jpa Mit en

2 – Hibernate, Java und das Java Persistence API – Ein Überblick

38

2.4 ZusammenfassungDas neue JPA vereinfacht den Umgang mit persistenten Daten erheblich. Gegenüber den unhandlichen EJB 2.x Entity Beans stellt dieser Standard eine deutliche Verbesserung in vielen Bereichen, wie beispielsweise dem Umgang mit Metadaten in Form von Annota-tions oder der Verwendung von POJOs als Entities, dar.

Das JPA ist dabei aber nicht als Konkurrenz zu Hibernate zu verstehen, sondern als das, was sie ist: ein API und keine fertige Implementierung. Nicht mehr und nicht weniger.

Bei der Entstehung des JPA wurden Erfahrungen aus bestehenden Persistenzlösungen wie Hibernate, TopLink, JDO und natürlich auch EJB 2.1 berücksichtigt. Daher über-rascht es auch nicht weiter, dass das Konzept des JPA der von Hibernate stark ähnelt und eine Implementierung dieses API durch Hibernate von Anfang an existierte.

Durch Kombination der drei Projekte Hibernate Annotations, Hibernate EntityManager und Hibernate Core erhält der Anwender eine vollständige JPA-Implementierung.

Die drei Projekte können jedoch auch beliebig kombiniert werden. So sind der Einsatz von Hibernate Annotations und Hibernate Core, also die Verwendung von Annotations zur Deklaration von Metadaten und die Programmierung gegen das normale Hibernate API, ohne Probleme möglich. Dem Entwickler bleibt somit die Wahl, ob er das JPA verwendet oder nicht.

Page 40: Jpa Mit en

JPA mit Hibernate 39

3 Einführung in Hibernate und JPA

In diesem Kapitel wird zu Beginn das Beispielprojekt vorgestellt. Es wird durchgängig im Buch verwendet und soll ausschließlich die Konzepte und Eigenschaften von Hiber-nate und JPA praxisnah erläutern. Am Ende des Buches hat man zwar keine fertige Anwendung in Händen, aber alle nötigen Grundlagen kennen gelernt, um sofort richtig durchzustarten.

Nach der Einführung zum Kontext der Beispiele werden in diesem Kapitel die grundle-genden Operationen zum Speichern, Laden und Aktualisieren vorgestellt. Dabei liegt das besondere Augenmerk auf den verschiedenen Arten des Einsatzes von Hibernate. Zu ihnen gehört das klassische Hibernate mit XML-Mapping, Hibernate mit Annota-tions, Hibernate als JPA Persistence Provider und der Einsatz von Hibernate in der Per-sistenzschicht eines Application Servers. Die Einführung in das klassische Hibernate mit XML-Mapping und Annotations dient in diesem Kapitel dem Verständnis der technolo-gischen Grundlagen, die hinter Hibernate stehen. Im weiteren Verlauf des Buches kon-zentriere ich mich auf den Einsatz von Hibernate als JPA Persistence Provider.

3.1 BeispielprojektAls Kontext für die Beispiele wurde eine Onlinebibliothek gewählt. Die Anwendung soll Taschen- und Hörbücher verwalten, Benutzer anlegen können und eine Suche zur Verfü-gung stellen. Dabei stehen die für JPA und Hibernate interessante Datenschicht und das Backend der Anwendung im Vordergrund.

3.1.1 Die Anwendungsfälle

Um möglichst viele Konzepte von JPA und Hibernate erläutern zu können, beschränke ich mich hier auf ein paar wenige Anwendungsfälle, die den Kontext der Beispiele bil-den.

Grundlegende Funktionen der Anwendung

� die Registrierung eines Benutzers

� das Anmelden eines Benutzers

� das Erfassen eines Buches oder Hörbuches

� die Suche nach Büchern

� die Ausleihe und Rückgabe eines Buchs

Page 41: Jpa Mit en

3 – Einführung in Hibernate und JPA

40

Um ein Buch ausleihen zu können, muss man als Benutzer registriert sein. Ein Buch kann mit den Angaben Buchname, ISBN, Verlag und Autor erfasst werden. Die Suchfunktion beinhaltet die Suche nach Titel, ISBN und Autor. Bei der Ausleihe eines Buches wird das Buch mit dem Benutzer „verknüpft“.

3.1.2 Das Klassendiagramm

Aufgrund der vorher genannten Anwendungsfälle ergeben sich für das Beispielprojekt folgende Klassen bzw. Entities:

� Benutzer mit Adresse

� Taschenbuch und Hörbuch

� Autor

� Verlag

Ein Benutzer hat einen eindeutigen Benutzernamen, wofür sich die E-Mail-Adresse besonders eignet. Des Weiteren wird noch die Adresse des Benutzers erfasst. Dadurch ergibt sich eine weitere Entity. Die Angaben Titel, Autoren, Verlag und eine eindeutige ISBN werden für ein Buch benötigt. Bei einem Hörbuch ist zusätzlich noch die Länge in Minuten und das Medium, z. B. CD oder DVD, angegeben. Eine Übersicht über die genannten Klassen befindet sich in Abbildung 3.1. Dabei handelt es sich hier lediglich um die Hauptklassen des Beispielprojekts. An gegebener Stelle wurden neue Klassen und Attribute eingefügt, um den Funktionsumfang und die Besonderheiten von JPA und Hibernate besser zeigen zu können.

Abbildung 3.1: Klassenmodell des Beispielprojekts

User-firstname: String-lastname: String-email: String

Publisher-description: String

Address-street: String-city: String

Book-title: String-ISBN: String-author: String

AudioBook-medium: String-lengthInMinutes: int

Paperback-pages: int

Page 42: Jpa Mit en

Beispielprojekt

JPA mit Hibernate 41

3.1.3 Projekt einrichten

In diesem Abschnitt werden kurz die verschiedenen Voraussetzungen erläutert, die für die Arbeit mit Hibernate und dem Beispielprojekt erforderlich sind. Neben Java als zugrunde liegender Programmiersprache wird eine Datenbank für die persistente Spei-cherung der Entitäten benötigt. Um Hibernate als Persistenzschicht im Application Ser-ver zu verwenden, muss natürlich auch ein solcher installiert und eingerichtet sein. Als Entwicklungsumgebung kann prinzipiell, je nach Geschmack, eine beliebige zum Ein-satz kommen. Das Beispielprojekt wurde mit NetBeans 6.81 entwickelt, da es recht gute Unterstützung für JPA und EJB3 bietet.

Java-Version

Für die Verwendung von Hibernate Annotations wird mindestens die Java Standard Edition (SE) 5.0 benötigt, da ab dieser Version Annotations zur Verfügung stehen. Das klassische Hibernate (ohne Annotations) kann jedoch auch mit Java SE 1.4 verwendet werden. Möchte man Hibernate noch mit Java SE 1.3 verwenden, so muss Hibernate neu kompiliert werden. Entsprechende Informationen findet man auch in der FAQ von Hibernate2.

Ist noch kein Java vorhanden, so empfiehlt sich der Download der neuesten Java-Version vom Sun Developer Network3.

Datenbank

Für die persistente Speicherung von Objekten muss eine Datenbank verfügbar sein. Hiber-nate unterstützt eine Vielzahl von Datenbanken4 z. B. Oracle, DB2, MS SQL Server, MySQL und PostgreSQL. Für das Beispielprojekt wird PostgreSQL5 verwendet, es kann jedoch auch jede andere von Hibernate unterstützte Datenbank benutzt werden. PostgreSQL ist ab Version 8 für Linux und Windows verfügbar. Eine ausführliche Dokumentation zur Installation und Konfiguration findet man unter http://www.postgresql.org/docs/. Mit dem Admin-Programm pgAdmin III (Abbildung 3.2) von PostgreSQL können ein neuer Daten-bankbenutzer und eine Datenbank für das Projekt sehr leicht angelegt werden.

Des Weiteren wird ein JDBC6-Treiber (Java Database Connectivity API) benötigt. JDBC bietet eine einheitliche Schnittstelle zu Datenbanken verschiedener Hersteller. Dank JDBC können mit Java SQL-Anfragen an die Datenbank gesendet und die Abfrageergeb-nisse ausgewertet werden. Einen JDBC-Treiber für PostgreSQL findet man unter http://jdbc.postgresql.org/.

1. NetBeans: http:// netbeans.org2. Hibernate-Dokumentation und FAQ: https://www.hibernate.org/5.html3. Java: http://java.sun.com/4. Von Hibernate unterstützte Datenbanken: http://www.hibernate.org/80.html5. PostgreSQL: http://www.postgresql.org/6. JDBC Technology Overview: http://java.sun.com/products/jdbc/overview.html

Page 43: Jpa Mit en

3 – Einführung in Hibernate und JPA

42

Abbildung 3.2: pgAdmin III

Application Server

Als Application Server kann prinzipiell jeder beliebige EJB3-kompatible Application Ser-ver installiert werden. Allerdings empfiehlt sich für das Beispielprojekt die Installation von Glassfish v3, da dieser mit NetBeans 6.8 gut zusammenarbeitet. NetBeans 6.8 und Glassfish v3 kann man im Bundle von http http://netbeans.org/downloads/index.html herun-terladen. Es empfiehlt sich, alle von Hibernate benötigten Bibliotheken in das lib-Ver-zeichnis des Application Servers zu kopieren, da es sonst zu Problemen führen kann.

Entwicklungsumgebung

Das Beispielprojekt steht inklusive aller Sourcen und benötigten Bibliotheken auf www.entwickler-press.de/jpa zur Verfügung. Nach dem Entpacken kann das Projekt, das aus fünf Subprojekten besteht (Tabelle 3.1), direkt in NetBeans 6.8 geöffnet werden:

Page 44: Jpa Mit en

Beispielprojekt

JPA mit Hibernate 43

Tabelle 3.2 erläutert kurz die Bibliotheken, die für die Arbeit mit Hibernate benötigt werden.

Logging

Um mögliche Fehler schnell zu entdecken, sollte noch das Logging eingerichtet werden. Im Beispielprojekt wird das weit verbreitete Open-Source-Logging-Framework Log4j7 verwen-det. In Listing 3.1 sieht man die Datei log4j.properties, die im Beispielprojekt verwendet wird und sich im Klassenpfad befinden muss. Mit dieser Konfiguration werden alle Logausgaben im Debug-Level von Hibernate in der Datei booksonline.log ausgegeben.

Subprojekt Beschreibung

booksonline-intro Projekt enthält die Beispiele aus diesem Kapitel für das klassische Hibernate, Hiber-nate mit Annotations und Hibernate als Persistence Provider für JPA.

booksonline Alle Beispiele der Kapitel 4 bis 9 sind in diesem Projekt zusammengefasst. In Tests kann die Funktionalität überprüft werden.

booksonline-appser-ver

EJB3-Anwendung, die booksonline-ejb und booksonline-war referenziert und daraus ein EAR zum Deployment auf dem Application Server erstellt. Es wird Hibernate als Persistenzschicht im Application Server verwendet.

booksonline-ejb Businesslogik mit den Entitäten und Web Services der EJB3-Anwendung.

booksonline-client Client für den Zugriff auf die EJB3 Anwendung via WebServices.

Tabelle 3.1: Subprojekte des Beispielprojekts für NetBeans 6.8

Bibliothek Modul Beschreibung

hibernate3.jar Core Kern von Hibernate, dieser hat viele Abhängigkei-ten zu anderen Bibliotheken, die ausführlich in der ReadMe-Datei im lib-Verzeichnis von Hibernate Core beschrieben werden.

hibernate-annotations.jar Annotations Implementierung der JPA 1.0 Annotations

ejb3-persistence.jar Annotations Interfaces des Java Persistence API 1.0

hibernate-commons-annotations.jar Annotations Zusätzliche Hibernate-spezifische Annotations und Helper-Klassen

hibernate-entitymanager.jar Entity Manager Implementierung des JPA 1.0 Entity Managers und zugehöriger Klassen

Tabelle 3.2: Benötigte Bibliotheken von Hibernate

7. Log4j: http://logging.apache.org/log4j/docs/index.html

log4j.rootLogger=DEBUGlog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern= %d %5p [%t] (%F:%L) - %m%n

Listing 3.1: log4j.properties

Page 45: Jpa Mit en

3 – Einführung in Hibernate und JPA

44

3.1.4 Testen

Zum Testen des Beispielprojekts empfiehlt sich der Einsatz eines entsprechenden Test-frameworks. Die zwei bekanntesten Vertreter für Java sind JUnit8 und TestNG9. Im Bei-spielprojekt wird JUnit in Version 4 eingesetzt und hauptsächlich zum Testen der Data-Access-Objekte (DAO) verwendet. Das Testframework ist einfach und übersichtlich. Zudem findet man dazu zahlreiche Einführungen im Internet.

In JUnit4 werden mit folgenden Annotations die Testmethoden markiert:

� @BeforeClass, @AfterClass: Die markierten Methoden werden vor bzw. nach allen Testmethoden aufgerufen, und eignen sich somit für allgemeine Initialisierungen.

� @Before, @After: Die markierten Methoden werden vor bzw. nach jedem Test aufge-rufen. Somit können für jeden Test die gleichen Rahmenbedingungen geschaffen werden.

� @Test: Die markierten Methoden sind die eigentlichen Tests und beinhalten den Test-code mit den assert-Methoden zur Überprüfung der Funktionalität.

3.2 Klassisches HibernateHibernate ist als Persistenzfamework seit Langem eine feste Größe in der Java-Welt. In den folgenden Abschnitten wird das klassische Hibernate vorgestellt. In den Hibernate-Versio-nen vor 3.1 konnten keine Annotationen, sondern ausschließlich XML-Dateien oder XDoc-let für das Mapping von Entities auf Datenbanktabellen verwendet werden. Die Möglich-keit des Mappings mittels XML-Datei steht aber auch in allen neuen Hibernate-Versionen zur Verfügung.

log4j.appender.BOOKS=org.apache.log4j.RollingFileAppenderlog4j.appender.BOOKS.File=booksonline.loglog4j.appender.BOOKS.MaxFileSize=512KBlog4j.appender.BOOKS.MaxBackupIndex=5log4j.appender.BOOKS.layout=org.apache.log4j.PatternLayoutlog4j.appender.BOOKS.layout.ConversionPattern=%d %p [%t] %c - %m%nlog4j.logger.org.hibernate=DEBUG, BOOKSlog4j.logger.booksonline=DEBUG, BOOKS

8. JUnit: http://www.junit.org/9. TestNG: http://testng.org/doc/index.html

Listing 3.1: log4j.properties (Forts.)

Page 46: Jpa Mit en

Klassisches Hibernate

JPA mit Hibernate 45

3.2.1 Hibernate-Konfiguration

Die Konfiguration von Hibernate kann mittels der hibernate.properties oder der hiber-nate.cfg.xml erfolgen. Im Beispielprojekt wird die Konfiguration mittels der XML-Datei vorgenommen.

<?xml version='1.0' encoding='utf-8'?><!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration- 3.0.dtd">

<hibernate-configuration> <session-factory> <!-- Database connection settings --> <property name="connection.driver_class"> org.postgresql.Driver </property> <property name="connection.url"> jdbc:postgresql://localhost:5432/booksonline </property> <property name="connection.username">booksonline</property> <property name="connection.password">db</property> <!-- SQL dialect for PostgreSQL --> <property name="dialect"> org.hibernate.dialect.PostgreSQLDialect </property> <!-- Disable the second-level cache --> <property name="cache.provider_class"> org.hibernate.cache.NoCacheProvider </property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Drop and re-create the database schema on startup --> <property name="hbm2ddl.auto">update</property> <!-- Mappings --> <mapping resource="booksonline/bo/User.hbm.xml"/> </session-factory></hibernate-configuration>

Listing 3.2: hibernate.cfg.xml

Page 47: Jpa Mit en

3 – Einführung in Hibernate und JPA

46

In der hibernate.cfg.xml in Listing 3.2 werden im Wesentlichen die Datenbankeinstellun-gen definiert: Die JDBC-Treiber-Klasse, der Datenbank-URL, der Username und das Passwort müssen angegeben werden. Des Weiteren wird ein entsprechender SQL-Dia-lekt ausgewählt, zum Beispiel für

� PostgreSQL: org.hibernate.dialect.PostgreSQLDialect

� MySQL (InnoDB): org.hibernate.dialect.MySQLInnoDBDialect

� Oracle 9i/10g: org.hibernate.dialect.Oracle9Dialect

� HypersonicSQL: org.hibernate.dialect.HSQLDialect

Eine komplette Liste aller Dialekte, die Hibernate zur Verfügung stellt, gibt es hier10. Einige Beispiele zur Datenbankkonfiguration sind auch in der hibernate.properties zu sehen, diese ist im Ordner /etc von Hibernate Core zu finden. Mit show_sql = true gibt Hibernate alle generierten SQLs auf der Konsole aus und mit hbm2ddl.auto = updateerstellt Hibernate beim Anwendungsstart automatisch die Tabellen, falls sie noch nicht existieren. Verwenden Sie hbm2ddl.auto = create, um alle Tabellen vor dem Erstellen zu löschen. Mit mapping werden die XML-Mappings für die Entities angegeben. Wie man diese Mappings erstellt, wird im nächsten Abschnitt erläutert.

Die Datei hibernate.cfg.xml muss sich im Klassenpfad befinden, damit Hibernate darauf zugreifen kann. Wenn die alternative Konfigurationsdatei hibernate.properties für die Konfiguration verwendet werden soll, muss sie dann ebenfalls im Klassenpfad vorhan-den sein.

3.2.2 Entity „User“

Nachdem Hibernate konfiguriert ist, kann die erste Klasse erstellt werden. Die Entity User soll den vollständigen Namen und die E-Mail des Benutzers enthalten.

10. Hibernate-SQL-Dialekte: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration.html#configuration-optional-dialects

package booksonline.bo;

public class User { private Long id; private String firstname; private String lastname; private String email;

public User() { }

public Long getId() { return id; }

Listing 3.3: User.java

Page 48: Jpa Mit en

Klassisches Hibernate

JPA mit Hibernate 47

Die Entity User (Listing 3.3) ist ein einfaches POJO11 und hat keine externen Abhängigkei-ten. Die Eigenschaften der Klasse haben Getter- und Setter-Methoden entsprechend der JavaBeans-Nameskonventionen, was empfohlen wird, aber kein Muss ist. Hibernate kann auch direkt auf die Felder zugreifen. Das Attribut id enthält einen eindeutigen Bezeichner, den alle Klassen, die persistent gespeichert werden sollen, benötigen. Des Weiteren schreibt Hibernate vor, dass ein Standard-Konstruktor vorhanden sein muss. Der Kon-struktor kann in Hibernate auch private sein, doch für eine JPA Entity ist lediglich publicund protected erlaubt.

Hibernate XML-Mapping

Mit dem XML-Mapping wird Hibernate mitgeteilt, wie die Attribute des Entities in den Tabellen der Datenbank abgebildet werden sollen. Die XML-Dateien haben die Endung .hbm.xml und es wird empfohlen, für jede Entity eine eigene XML-Datei zu erstellen, da es sonst sehr schnell unübersichtlich werden kann.

In Listing 3.4 ist das Mapping User.hbm.xml für die Entity User zu sehen.

private void setId(Long id) { this.id = id; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } ...}

11. Plain Old Java Object: http://www.martinfowler.com/bliki/POJO.html

<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping package="booksonline.bo"> <class name="User" table="T_USER"> <id name="id"> <generator class="native"/> </id> <property name="firstname"/> <property name="lastname"/> <property name="email"/> </class></hibernate-mapping>

Listing 3.4: User.hbm.xml

Listing 3.3: User.java (Forts.)

Page 49: Jpa Mit en

3 – Einführung in Hibernate und JPA

48

Das Mapping beginnt immer mit <hibernate-mapping>, dort wird unter anderem das package der Klasse definiert. Bei Nichtangabe des package muss der vollqualifizierte Klas-senname im Element <class> angegeben werden. Das Attribut table gibt den Name der Tabelle für die Speicherung der User an. Das Element <id> definiert das Feld der Klasse User, das als Primärschlüssel verwendet werden soll. Mit <generator> wird die Strategie zur Generierung des eindeutigen Schlüssels gesetzt, damit er nicht „per Hand“ verwaltet werden muss. Im Beispiel wird mit native der Default-Generator spezifiziert. Dieser wählt entsprechend der darunter liegenden Datenbank eine geeignete Strategie zur Generierung des Primärschlüssels. Bei Oracle oder PostgreSQL kommen in diesem Fall Sequences zum Einsatz. Mit <property> werden alle Felder angegeben, die persistent gesichert werden sollen.

Weiterhin gibt es die Möglichkeit mittels XDoclet12 aus speziellen Tags im JavaDoc einer Klasse das XML-Mapping zu generieren. Mit Einführung von Annotations für das Map-ping ist dieses Vorgehen allerdings nicht mehr nötig.

3.2.3 HibernateUtil

Damit durch Hibernate ein Objekt gesichert, geladen oder aktualisiert werden kann, wird eine Session benötigt. Eine Session erhält man von einer SessionFactory. Die SessionFactory wird aus der Konfiguration von Hibernate beim Start der Anwendung einmal erzeugt und sollte zur gesamten Laufzeit der Anwendung erreichbar sein. Eine Session ermöglicht den Zugriff auf Datenbankverbindungen und Transaktionen und bie-tet die CRUD-Operationen (create, read, update und delete) an. Damit zur Laufzeit der Zugriff auf eine Session möglichst leicht ist, wird eine Klasse HibernateUtil erstellt (Lis-ting 3.5), die mit einer statischen Methode eine Session zurückgibt.

12. XDoclet: http://xdoclet.sourceforge.net/xdoclet/index.html

package booksonline.util;import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;

public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { // Create the SessionFactory from hibernate.cfg.xml sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { System.err.println("SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } }

Listing 3.5: HibernateUtil.java

Page 50: Jpa Mit en

Klassisches Hibernate

JPA mit Hibernate 49

3.2.4 Data Access Object

Das Anlegen, Lesen, Aktualisieren und Löschen von Entities sollte zentral über ein Data Access Object (DAO) vorgenommen werden. Dabei ist es üblich, pro Entity ein entspre-chendes DAO anzulegen, damit die Übersichtlichkeit gewahrt bleibt. Die DAOs unter-scheiden sich in den meisten Fällen lediglich um den Typ der zu speichernden Klasse. Aus diesem Grund ist das Anlegen eines generischen DAOs oftmals sinnvoll.

Entity „User“ sichern

Mit der Methode persist aus dem Beispielcode in Listing 3.6 wird eine Entity User persis-tent in der Datenbank gesichert. Dafür wird eine Session über die Hilfsklasse HibernateU-til geöffnet. Eine Transaktion wird gestartet und das Objekt User wird der Session über-geben. Nach dem Beenden der Transaktion und der Session ist das Objekt in der Datenbank abgelegt.

Der entscheidende Aufruf ist session.persist(user), da an dieser Stelle das Objekt der Session übergeben wird. Hibernate generiert und setzt die ID und schreibt das Objekt in die Datenbank.

public static SessionFactory getSessionFactory() { return sessionFactory; }}

package booksonline.dao;import booksonline.bo.User;import booksonline.util.HibernateUtil;import org.hibernate.Session;import org.hibernate.Transaction;

public class UserDao { public void persist(User user) { Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tx = session.beginTransaction(); session.persist(user); tx.commit(); session.close(); }...}

Listing 3.6: UserDao.java persist()

Listing 3.5: HibernateUtil.java (Forts.)

Page 51: Jpa Mit en

3 – Einführung in Hibernate und JPA

50

Der Aufruf der persist-Methode des DAOs kann wie im Folgenden gezeigt durchge-führt werden:

Das SQL, das Hibernate an die Datenbank im obigen Beispiel weitergeben wird, sieht wie folgt aus:

Das Insert-Statement wird nicht schon beim Aufruf von session.persist() an die Daten-bank geschickt, sondern erst beim Beenden der Transaktion. Alternativ kann auch Ses-sion.save() oder Session.saveOrUpdate() zur Sicherung eines Objekts aufgerufen werden. Während sich save() wie persist() verhält, überprüft saveOrUpdate(), ob die ID gesetzt ist oder nicht und führt dann entsprechend ein save() oder ein update() zum Aktualisie-ren eines Objekts durch. Das Aktualisieren von Objekten wird im übernächsten Abschnitt erläutert.

Entity „User“ laden

Ebenso einfach wie das Sichern von Objekten funktioniert das Laden von Objekten. In Listing 3.7 wird wiederum eine Session geöffnet und eine Transaktion gestartet. Mit der Session wird mittels get(Class class, Serializable id) oder load(Class class, Serial-izable id) ein vorher gesicherter User geladen. Beide Methoden benötigen die ID des zu ladenden Datensatzes. Falls die ID nicht bekannt ist, kann mit einem Datenbanktool, zum Beispiel dem DbVisualizer Free13, in der Datenbank nachgesehen werden. In der Praxis ist natürlich die Verwendung eines Datenbanktools zum Ermitteln des Primärschlüssels nicht nötig. Stattdessen kommt zum Beispiel die komfortable Java Persistence Query Lan-guage (JPQL) zum Einsatz (Kapitel 7).

UserDao userDao = new UserDao();User user = new User("Moritz", "Muster", "[email protected]");userDao.persist(user);

insert into T_USER (firstname, lastname, email, id)values ('Moritz', 'Muster', '[email protected] ', 1)

13. DbVisualizer Free: http://www.dbvis.com/products/dbvis/download/

...public class UserDao { ... public User findById(Long id) { Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tx = session.beginTransaction(); User user = (User) session.get(User.class,id); tx.commit(); session.close();

Listing 3.7: UserDao.java get()

Page 52: Jpa Mit en

Klassisches Hibernate

JPA mit Hibernate 51

Beim beispielhaften Aufruf der Methode findById wird im Folgenden angenommen, dass der User aus dem vorherigen Beispiel die ID „1“ bekommen hat.

In Listing 3.7 wird get() zum Laden eines Objekts verwendet, mit load() können eben-falls Objekte per ID geladen werden. Es gibt allerdings ein paar wesentliche Unter-schiede zwischen get() und load(), die unbedingt beachtet werden sollten:

� get() gibt immer null zurück, falls das Objekt nicht existiert, load() hingegen wirft eine Exception.

� load() kann einen so genannten Proxy zurückgeben, darunter versteht man einen Platzhalter für das eigentliche Objekt. Sobald ein Zugriff auf den Proxy erfolgt, wird das eigentliche Objekt nachgeladen (erst dann gibt es einen Datenbankzugriff, falls nötig). Falls das Proxy aus dem Cache gebildet wurde, kann beim Nachladen immer noch festgestellt werden, dass das Objekt nicht existiert, worauf eine Exception geworfen wird.

Die Frage ist nun, wann get() und wann load() benutzt werden sollte. Dies hängt ganz von der Situation ab; wenn sicher ist, dass das Objekt existiert, kann load() ohne Beden-ken verwendet werden. Falls nicht, ist man mit get() auf der sicheren Seite.

Entity „User“ aktualisieren

In Listing 3.8 wird mit der Methode update(Object entity) ein bereits persistentes Objekt aktualisiert.

return user; }...

UserDao userDao = new UserDao();User user = userDao.findById(1L);

...public class UserDao {... public void update(User user) { Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tx = session.beginTransaction(); session.update(user); tx.commit(); session.close(); }...

Listing 3.8: UserDao.java update()

Listing 3.7: UserDao.java get() (Forts.)

Page 53: Jpa Mit en

3 – Einführung in Hibernate und JPA

52

Der Aufruf von session.update() ist nötig, da sich die Entity User noch nicht in der Ver-waltung durch JPA und Hibernate befindet. Näheres zu dem Lebenszyklus und der Ver-waltung der Entities durch Hibernate befindet sich in Kapitel 5.

In Listing 3.9 wurde bereits die Entity User geladen. Dadurch erkennt Hibernate von selbst, dass das Objekt aktualisiert werden muss, und führt das Update beim Commit der Transaktion automatisch aus.

Unabhängig davon, ob das Update automatisch oder explizit angestoßen wurde, Hiber-nate generiert folgendes Update-Statement und gibt es an die Datenbank weiter:

Entity „User“ löschen

Auch das Löschen einer Entity kann einfach mit einem Methodenaufruf auf der Sessiondurchgeführt werden. Der entsprechende Code wird in Listing 3.10 gezeigt.

Zu beachten ist, dass die übergebene Entity User mindestens einen entsprechenden Pri-märschlüssel gesetzt haben muss, damit das gewünschte Objekt gelöscht werden kann, da das SQL-Delete-Statement anhand des Primärschlüssels ausgeführt wird. Folgendes Codefragment zeigt beispielhaft den Aufruf der Löschmethode:

User user = (User) session.get(User.class, 1L); user.setEmail("newemail@..."); // session.update(user); // unnötig

Listing 3.9: UserDao.java update() nach dem Laden nicht nötig

update T_USER set firstname=?, ... where id=?

...public class UserDao {... public void delete(User user) { Session session = HibernateUtil.getSessionFactory().openSession(); Transaction tx = session.beginTransaction(); session.delete(user); tx.commit(); session.close(); }...

Listing 3.10: UserDao.java delete()

UserDao userDao = new UserDao();User user = user.setId(1L);userDao.delete(user);

Page 54: Jpa Mit en

Klassisches Hibernate

JPA mit Hibernate 53

3.2.5 Testen des DAOs

Die Testklasse für das DAO wird in Listing 3.11 gezeigt. Der Test geht davon aus, dass die entsprechende Tabelle für die Entity User in der Datenbank leer ist, und somit als erste ID „1“ vergeben wird. In der Praxis sind solche „starken“ Testvoraussetzungen natürlich nicht geeignet, jedoch zum Zeigen der CRUD-Methoden an dieser Stelle durchaus akzeptabel.

package booksonline.test;public class UserTest { public UserTest() { } ... @Test public void testUser() { UserDao userDao = new UserDao(); User user = null; //Prüfen, dass kein User mit ID 1 in DB assertNull(userDao.findById(1L)); //neuen User anlegen und speichern user = new User("Moritz", "Muster", "[email protected]"); userDao.persist(user); //generierten Schlüssel überprüfen assertEquals(1L, user.getId().longValue()); //User in DB finden User = userDao.findById(1L); assertEquals("Moritz", user.getFirstname()); //User ändern und erneut speichern user.setFirstname("Max"); userDao.update(user); //User erneut finden und überprüfen user = userDao.findById(1L); assertEquals("Max", user.getFirstname()); //User löschen User userDelete = new User(); userDelete.setId(1L); userDao.delete(userDelete); //User wird nicht mehr gefunden assertNull(userDao.findById(1L)); }}

Listing 3.11: Testklasse für das DAO

Page 55: Jpa Mit en

3 – Einführung in Hibernate und JPA

54

3.3 Hibernate mit AnnotationsDie mit Java 5 eingeführten Annotations haben seit Version 3.1 auch in Hibernate Einzug gehalten. Durch die Annotations wird die XML-Konfiguration für das Mapping unnötig. Ein weiterer Vorteil ist die so genannte „Convention over Configuration“, wodurch nur die von den Standardparametern abweichenden Angaben mit Annotations angegeben werden müssen. Alle weiteren Einstellungen werden als gegeben angenommen. In Kapi-tel 4 werden die entsprechenden Defaults noch näher beleuchtet.

3.3.1 Hibernate-Konfiguration

Die Hibernate-Konfiguration bleibt wie gehabt identisch zu der in Kaptitel 3.2.1. Jedoch wird das Resource-Mapping nicht mehr auf die XML-Konfigurationsdatei bezogen, son-dern direkt auf die Klasse, da dort die Konfigurationen mittels der Annotations stattfin-den. In Listing 3.12 wird der betroffene Ausschnitt aus der hibernate.cfg.xml gezeigt.

3.3.2 Entity „User“

In Listing 3.13 wird die Entity User mit durch Annotations hinzugefügten Mapping-Infor-mationen vorgestellt.

... <! - Mappings Hibernate Classic --> <!-- <mapping resource="booksonline/bo/User.hbm.xml"/> --> <mapping class="booksonline.bo.User"/> ...

Listing 3.12: Änderungen an der Hibernate-Konfiguration

package booksonline.bo;...

@Entity@Table(name="T_User")public class User { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; private String firstname; private String lastname; private String email;

public User() { }

Listing 3.13: User.java mit Annotations

Page 56: Jpa Mit en

Hibernate mit Annotations

JPA mit Hibernate 55

Mit der Annotation @Entity wird die Klasse User als Entity markiert. Die Annotation @Table spezifiziert Eigenschaften der Datenbanktabelle, in die Objekte der Klasse Usergespeichert werden. In unserem Beispiel wird der Name der Tabelle auf T_User festge-legt. Die Annotation @Id entspricht dem gleichlautenden Element der XML-Mapping-Datei und spezifiziert den Primärschlüssel der Entity. Mit @GeneratedValue wird die Gene-ratorstrategie angegeben, welche mit GenerationType.AUTO denselben Default-Generator wie <generator class="native"/> referenziert. Besonders wichtig ist, dass die Attribute nicht speziell annotiert werden müssen, damit sie persistiert werden. Dies geschieht ohne weiteres Zutun automatisch. Möchte man ein Attribut jedoch als transient markie-ren, muss man die Annotation @Transient verwenden. Transiente Attribute werden nicht persistent in der Datenbank gespeichert.

3.3.3 HibernateUtil

Die Klasse HibernateUtil ändert sich bei Verwendung von Annotations nur marginal zum klassischen Hibernate. Lediglich die Klasse Configuration zum Aufruf der stati-schen Methode configure() muss in AnnotationConfigration geändert werden. In folgen-dem Codebeispiel wird die kleine Änderung gezeigt:

3.3.4 Data Access Object

Das Data Access Object ändert sich nicht durch die Verwendung der Annotations für das Mapping. Die Session kann wie beim klassischen Hibernate (Kapitel 3.2.4) mit den Methoden persist, update, delete und get zum Speichern, Aktualisieren, Löschen und Finden von Entities verwendet werden.

3.3.5 Testen des DAOs

Das Testen des DAOs erfolgt analog zum Test aus Kapitel 3.2.5.

public Long getId() { return id; } private void setId(Long id) { this.id = id; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } ...}

// Erzeugen der sessionFactory bei der Verwendung von AnnotationssessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();

Listing 3.13: User.java mit Annotations (Forts.)

Page 57: Jpa Mit en

3 – Einführung in Hibernate und JPA

56

3.4 Hibernate als JPA Persistence ProviderHibernate implementiert vollständig die JPA-1.0-Spezifikation, die mit EJB3 als Stan-dard-API für die Persistenz eingeführt wurde. JPA lässt sich sowohl „standalone“ in einer Desktopanwendung verwenden als auch als Persistenzschicht in einem Applica-tion Server. Im folgenden Kapitel soll zuerst die von einem Application Server unabhän-gige Verwendung vorgestellt werden. In Kapitel 3.5 wird dann Hibernate als Persistenz-schicht im Glassfisch v3 Application Server benutzt.

3.4.1 Konfiguration des Persistence Providers

Die Konfiguration des Persistence Providers für JPA wird in der Datei persistence.xmlvorgenommen. Neben den Einstellungen für JPA werden auch die Hibernate-spezifi-schen Attribute hier gesetzt, um Hibernate an die Bedürfnisse des Projekts anzupassen. In Listing 3.14 wird die Datei persistence.xml gezeigt, die sich im Verzeichnis META-INF befinden muss.

In der Datei wird am Anfang neben dem allgemeinem XML-Header die persistence-unitmit einem eindeutigen Namen definiert. Der Transaktionstyp wird auf RESOURCE_LOCALgesetzt, dadurch wird nicht das JTA (Java Transaction API14), sondern eine andere Im-plementierung verwendet. In unserem Fall stellt Hibernate den Transaktionsmechanis-mus zur Verfügung. Im Element provider wird Hibernate letztendlich als Persistence

<?xml version="1.0" encoding="UTF-8"?><persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" ... <persistence-unit name="booksonlinePU" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.connection.username" value="booksonline"/> <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/> <property name="hibernate.connection.password" value="db"/> <property name="hibernate.connection.url" value="jdbc:postgresql://localhost:5432/booksonline"/> <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> <property name="hibernate.show_sql" value="true"/> </properties> </persistence-unit></persistence>

Listing 3.14: persistence.xml

14. JTA: http://jcp.org/en/jsr/detail?id=907

Page 58: Jpa Mit en

Hibernate als JPA Persistence Provider

JPA mit Hibernate 57

Provider gesetzt. Die Einstellungen im Tag properties sind analog zu den Einstellungen in der hibernate.cfg.xml aus Kapitel 3.2.1.

3.4.2 Die Entity „User“

An der Entity User muss im Vergleich zu Kapitel 3.3.2 keine Änderung vorgenommen werden, da die verwendeten Annotations bereits JPA-kompatibel sind. Weitere Konfigu-rationsmöglichkeiten sowie Beziehungen zwischen Entities werden in den folgenden Kapiteln noch ausführlich behandelt.

3.4.3 HibernateUtil

In der Klasse HibernateUtil wird nicht mehr eine SessionFactory in einem statischen Sin-gleton gespeichert, sondern eine EntityManagerFactory. In Listing 3.15 wird der entspre-chende Code zum Anlegen einer solchen Factory gezeigt.

Neu in JPA 2.0

Die Properties für die Konfiguration der Datenbankverbindung in Java-SE-Umgebungen wurde in JPA 2 standardisiert. Statt die herstellerspezifischen Properties wie hibernate.connection.url oder hibernate.connection.usernamezu verwenden, stehen nun folgende Properties zur Verfügung:

� javax.persistence.jdbc.driver: voll qualifizierter Klassenname des JDBC-Treibers

� javax.persistence.jdbc.url: der treiberspezifische URL

� javax.persistence.jdbc.user: der Benutzername für die Datenbankverbindung

� javax.persistence.jdbc.password: das Passwort für die Datenbankverbindung

package booksonline.util;

import javax.persistence.EntityManagerFactory;import javax.persistence.Persistence;

public class HibernateJpaUtil { private static final EntityManagerFactory emf; static { try { emf = Persistence.createEntityManagerFactory("booksonlinePU"); } catch (Throwable ex) {

Listing 3.15: HibernateJpaUtil.java für das Anlegen einer EntityManagerFactory

i

Page 59: Jpa Mit en

3 – Einführung in Hibernate und JPA

58

Beim Aufruf der createEntityMangerFactory-Methode wird der Name der Persitence-Unit aus der Datei persistence.xml angegeben.

3.4.4 Data Access Object

Das DAO muss komplett angepasst werden, da nun der EntityManager anstatt der Hibernate-Session verwendet wird. Es werden im Folgenden analoge Methoden zu jenen aus Kapitel 3.2.4 vorgestellt.

Entity „User“ sichern

Das Sichern der Entity User erfolgt mit der Methode persist() des EntityManagers. Lis-ting 3.16 zeigt das entsprechende Codebeispiel.

Der EntityManager wird mittels der EntityMangerFactory erstellt. Nach dem Öffnen einer Transaktion wird die Entity User an die Mehtode persist() übergeben. Beim Commit der Transaktion erfolgt das Speichern der Entity in der Datenbank.

Entity „User“ laden

Das Laden einer Entity User erfolgt nahezu identisch im Vergleich zum klassischen Hibernate. Lediglich die Verwendung des EntityManagers und der Methodenname dif-ferieren. Listing 3.17 zeigt das entsprechende Codebeispiel aus dem DAO.

System.err.println("Initial EntityManagerFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } Public static EntityManagerFactory getEntityManagerFactory() { return emf; } }

public void persist(UserJpa user) { EntityManager em = HibernateJpaUtil.getEntityManagerFactory().createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); em.persist(user); tx.commit(); em.close();}

Listing 3.16: Sichern der Entity „User“ mittels EntityManager

Listing 3.15: HibernateJpaUtil.java für das Anlegen einer EntityManagerFactory (Forts.)

Page 60: Jpa Mit en

Hibernate als JPA Persistence Provider

JPA mit Hibernate 59

Entity „User“ aktualisieren

Das Aktualisieren der Entity User wird in JPA mit der Methode merge() vorgenommen. In Listing 3.18 wird die entsprechende Operation des DAO gezeigt.

Die Werte des Objekts werden übernommen und in der Datenbank gespeichert. Sollte die Entity User bereits in der Verwaltung des EntityManagers stehen, werden die Änderun-gen am Objekt automatisch mit Beenden der Transaktion gespeichert. Der Aufruf der Methode merge() ist dann nicht nötig. Dieses Verhalten ist analog zum Aktualisieren des Objekts im klassischen Hibernate (Kapitel 3.2.4).

Entity „User“ löschen

Das Löschen einer Entity erfolgt mit der Methode remove(). Das Codebeispiel in Listing 3.19 zeigt den Einsatz von remove() in der entsprechende DAO-Methode.

public User findById(Long id) { EntityManager em = HibernateJpaUtil.getEntityManagerFactory().createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user = em.find(User.class, id); tx.commit(); em.close(); return user;}

Listing 3.17: Sichern der Entity „User“ mittels EntityManager

public void update(UserJpa user) { EntityManager em = HibernateJpaUtil.getEntityManagerFactory().createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); em.merge(user); tx.commit(); em.close(); return user;}

Listing 3.18: Aktualisieren der Entity „User“ mittels EntityManager

public void delete(UserJpa user) { EntityManager em = HibernateJpaUtil.getEntityManagerFactory().createEntityManager();

Listing 3.19: Löschen der Entity „User“ mittels EntityManager

Page 61: Jpa Mit en

3 – Einführung in Hibernate und JPA

60

Das an remove() übergebene Objekt muss sich jedoch bereits in der Verwaltung des Enti-tyManagers befinden, um gelöscht werden zu können. Aus diesem Grund wird das Objekt mittels find() geladen und direkt als Parameter an remove() weitergeleitet.

3.4.5 Testen des DAOs

Das Testen des DAOs erfolgt analog zum Test aus Kapitel 3.2.5.

3.5 Hibernate als Persistenzschicht im Application Server

Der Einsatz von JPA , als Teil der EJB3-Spezifikation, ist grundsätzlich für die Verwen-dung im Application Server konzipiert. Hibernate füllt als konkrete Implementierung des Java Persistence API diese Position ebenfalls aus.

3.5.1 Konfiguration des Persistence Providers

Die Konfiguration des Persistence Providers erfolgt für den Einsatz im Application Ser-ver in der Datei persistence.xml (Listing 3.20) im Verzeichnis META-INF.

EntityTransaction tx = em.getTransaction(); tx.begin(); em.remove(em.find(UserJpa.class, user.getId())); tx.commit(); em.close(); return user;}

<?xml version="1.0" encoding="UTF-8"?><persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" ... <persistence-unit name="booksonlineEJB3PU" transaction-type="JTA"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>jdbc/booksonline</jta-data-source> <properties> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> </properties> </persistence-unit></persistence>

Listing 3.20: persistence.xml für die Konfiguration des Persistence Providers im Application Server

Listing 3.19: Löschen der Entity „User“ mittels EntityManager (Forts.)

Page 62: Jpa Mit en

Hibernate als Persistenzschicht im Application Server

JPA mit Hibernate 61

Der Aufbau der Konfigurationsdatei ist analog zu jener aus Kapitel 3.4.1. Jedoch sind zwei wesentliche Unterschiede vorhanden. Bei der Definition der persistence-unit wird der Transaktionstyp auf JTA gesetzt. Dadurch übernimmt der Application Server die Transaktionssteuerung. Ein manuelles Öffnen und Schließen einer Transaktion im Code ist nicht mehr nötig (Kapitel 3.5.4). Der weitere Unterschied ist die Konfiguration der Datenbankverbindung. Diese wird durch eine JTA-Datenquelle definiert. Die Datenquel-len werden im Application Server meist in der Administrationsoberfläche festgelegt. Dadurch muss bei einer Änderung der Datenbankverbindung nicht die persistence.xmlangepasst werden.

3.5.2 Die Entity „User“

Die Entity User ist analog zu denen aus den Kapiteln 3.3.2 und 3.4.2. Es müssen keine Änderungen vorgenommen werden. So können die gleichen Entities sowohl im Applica-tion Server als auch in einer Standalone-Desktop-Anwendung genutzt werden.

3.5.3 HibernateUtil

Nicht nur die Verwaltung der Transaktionen übernimmt der Application Server, auch die Bereitstellung des EntityManagers wird übernommen. Aus diesem Grund wird die Klasse HibernateUtil für dessen Bereitstellung nicht mehr benötigt.

3.5.4 SessionBean als Data Access Object

Um die Verwaltung der Transaktionen und EntityManager des Application Server ver-wenden zu können, muss eine SessionBean als Data Access Object genutzt werden. Näheres zu SessionBeans und EJB3 kann man im Java EE Tutorial15 oder in der Spezifi-kation16 nachlesen. In Listing 3.21 wird die zustandslose SessionBean UserFacadeBeangezeigt.

15. Java EE Tutorial: http://java.sun.com/javaee/6/docs/tutorial/doc/16. EJB-3-Spezifikation: http://jcp.org/en/jsr/detail?id=220

package booksonline.ws;

@Statelesspublic class UserFacadeBean implements UserFacadeRemote { @PersistenceContext private EntityManager em; public User persist(User user) { em.persist(user); Return user; } public User findById(Long id) {

Listing 3.21: Stateless SessionBean UserFacadeBean als Data Access Object

Page 63: Jpa Mit en

3 – Einführung in Hibernate und JPA

62

Die SessionBean UserFacadeBean ist mit der Annotation @Stateless markiert und imple-mentiert das Interface UserFacadeRemote, das in Listing 3.22 zu sehen ist. Im Interface wer-den alle Methoden definiert, die durch die SessionBean zur Verfügung gestellt werden.

Der EntityManager wird mittels Dependency Injection17 durch den Application Server in der SessionBean gesetzt. Das private Attribut em vom Typ EntityManager wird zu die-sem Zweck mit der Annotation @PersistenceContext markiert.

Der Aufruf der Methoden des EntityManagers zum Speichern, Laden, Aktualisieren und Löschen ist analog zu jenen aus Kapitel 3.4.4. Allerdings kann, wie bereits erwähnt, das manuelle Erzeugen des EntityManagers und das Aufbauen der Transaktion weggelassen werden. Mit jedem Methodenaufruf der SessionBean wird dies automatisch vom Applica-tion Server übernommen.

User user = em.find(User.class, 1L); Return user; } public void delete(User user) { em.remove(em.find(User.class, user.getId())); } public void update(User user) { em.merge(user); }}

package booksonline.ws;

@Remotepublic interface UserFacadeRemote { public User persist(User user); public User findById(Long id); public void delete(User user); public void update(User user);}

Listing 3.22: Remote Interface für die SessionBean

17. Dependency Injection: http://de.wikipedia.org/wiki/Dependency_Injection

Listing 3.21: Stateless SessionBean UserFacadeBean als Data Access Object (Forts.)

Page 64: Jpa Mit en

Zusammenfassung

JPA mit Hibernate 63

3.5.5 Testen der SessionBean mit Web-Service-Schnittstelle

Da die SessionBean im Application Server ausgeführt wird, kann man für den Test nicht direkt auf sie zugreifen. Mit einer Web-Service-Schnittstelle umgeht man diese Barriere und kann die Funktionalität wie gewohnt Testen.

Das Bereitstellen einer Web-Service-Schnittstelle ist mittels EJB3 sehr einfach. Es muss lediglich die SessionBean mit der Annotation @WebService markiert werden. Dadurch werden alle zugehörigen Methoden der SessionBean als Web Service zur Verfügung gestellt. Die entsprechende WSDL kann, je nach Application Server, im Administrations-bereich angezeigt und exportiert werden.

NetBeans 6.8 beinhaltet eine einfache Möglichkeit, mittels einer vorhandenen WSDL einen Web-Service-Client zu erzeugen. Mit dem Client kann man wie in Listing 3.23gezeigt auf die UserFacadeBean zugreifen.

Der weitere Test erfolgt analog zu jenem aus Kapitel 3.2.5.

3.6 ZusammenfassungIn diesem Kapitel wurde zur Einführung von Hibernate und JPA ein einfaches Beispiel-projekt vorgestellt, das im Laufe des Buchs weiterhin als Referenz für Beispiele dient. In den weiteren Abschnitten wurde die nötige Infrastruktur für das Beispiel erläutert. Das erste persistente Objekt, der User, wurde mit wenig Aufwand gesichert, geladen, aktuali-siert und gelöscht. Dabei wurden ausführlich die verschiedenen Arten des Einsatzes von Hibernate beleuchtet: das klassische Hibernate, Hibernate mit Annotations, Hibernate als JPA Persistence Provider und Hibernate als Persistenzschicht im Application Server. Die für jeden Einsatzbereich entwickelten DAOs wurden mittels JUnit4 getestet. Für den Test der UserFacadeBean, dem DAO im Application Server, war die Bereitstellung eines Web Services nötig.

...UserFacadeBeanService service = new UserFacadeBeanService()UserFacadeBean userFacade = service.getUserFacadeBeanPort();assertNull(userFacade.findById(1L));...

Listing 3.23: Zugriff auf UserFacadeBean mittels Web-Service-Client

Page 65: Jpa Mit en
Page 66: Jpa Mit en

JPA mit Hibernate 65

4 Der Aufbau und das Mapping von Entities

Die Einführung in Kapitel 3 hat die Grundfunktionen und verschiedenen Anwendungs-möglichkeiten von Hibernate mit und ohne JPA anhand des Objekts User gezeigt. Das Objekt kann in der Datenbank gesichert, aus der Datenbank geladen und auch wieder gelöscht werden. Dieses Kapitel beschäftigt sich nun ausführlich mit dem Aufbau von Entities. Zuerst werden die allgemeinen Anforderungen an eine Entity erläutert und die Möglichkeiten für die Generierung von Primärschlüsseln aufgezeigt. Anschließend wer-den Objekte als Komponenten oder Assoziationen miteinander verbunden. Außerdem werden die Möglichkeiten gezeigt, wie ein Objektmodell mit Vererbung in der Daten-bank gemappt werden kann. Den Abschluss bildet die Abbildung von Collections aus einfachen Datentypen in Entity-Klassen mit Hibernate, da JPA 1.0 hier keine Möglichkei-ten bietet. Allerdings wurde die Spezifikation des JPA in der Version 2.0 an dieser Stelle erweitert. Wie bereits in Kapitel 3 erwähnt, soll im Folgenden hauptsächlich auf die Mög-lichkeiten des JPA eingegangen werden. Dabei werden aber auch die Spezialitäten und Besonderheiten von Hibernate mit berücksichtigt.

4.1 Anforderungen an eine EntityEine Entity ist, wie bereits in der Einführung erwähnt, ein einfaches Java-Objekt (POJO). Allerdings muss sie dennoch einige Bedingungen erfüllen:

� Die Entity-Klasse muss mit der Annotation @Entity markiert werden, um sie als verwaltete Instanz zu kennzeichnen.

� Die Entity-Klasse muss einen parameterlosen Konstruktor enthalten, der public oder protected ist. Die Klasse kann beliebige weitere Konstruktoren enthalten.

� Die Entity-Klasse muss eine Top-Level-Klasse sein. Die Definition eines Enum oder Interface als Entity-Klasse ist demnach nicht möglich. Jedoch können sowohl abs-trakte als auch konkrete Klassen als Entity markiert werden. Einziger Unterschied ist, dass eine abstrakte Klasse nicht direkt instanziiert werden kann.

� Die Entity-Klasse darf nicht als final deklariert sein. Ebenso dürfen Methoden oder persistente Attribute der Klasse nicht final sein.

� Die Entity-Klasse muss mit dem Serializable-Interface markiert werden, falls Instan-zen der Klasse „by-value“, bspw. via Remote-Interface, übertragen werden sollen.

� In jeder Entity-Klasse muss ein Primärschlüssel enthalten sein, er kann auch mittels Vererbung von der Superklasse übernommen werden.

Page 67: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

66

Die persistenten Attribute einer Klasse können mit Metadaten und weiteren Informati-onen versehen werden. Dies bestimmt auch, in welcher Art auf die Attribute der Entity zugegriffen wird.

Es stehen zwei Methoden zur Verfügung: Direkte Markierung und Zugriff der Klassen-attribute oder Markierung und Zugriff der Getter-Methoden. Die Art der Markierung persistenter Attribute muss innerhalb einer Klassenhierarchie gleich sein. Es dürfen also nicht beide Arten gemischt in Super- und Subklassen innerhalb derselben Klassen-hierarchie verwendet werden.

Der Vorteil bei der Markierung der Getter-Methoden besteht darin, dass in den Metho-den weitere Businesslogik, beispielsweise für die Validierung, eingebaut werden kann. Dieser Code wird dann durch den Persistence Provider, in unserem Fall Hibernate, ausgeführt. Dabei ist allerdings zu beachten, dass die Reihenfolge der Ausführung der Methoden nicht vorhersagbar ist und somit keine Abhängigkeiten zwischen den ein-zelnen Getter-Methoden bestehen sollten.

Der Vorteil bei der direkten Markierung der Klassenattribute mit den Metainformatio-nen besteht darin, dass keine Getter-Methoden definiert werden müssen. Dadurch kann ein Zugriff auf die Attribute von „außen“ verhindert werden, da die Getter-Methoden für die Markierung mit Metadaten public oder protected sein müssen.

Neu in JPA 2.0

Um die Vorteile beider Zugriffs- und Markierungsvarianten nutzen zu kön-nen, kann mit JPA 2.0 der Typ pro Entity mit der Annotation @Access definiert werden. Dabei wird FIELD für den Zugriff auf Instanzvariablen und PROPERTYfür Verwendung der Getter-Methoden der Annotation übergeben. Annotiert man eine Entity mit @Access, ändert sich jedoch nicht der Default für die Klas-senhierarchie.

Aber nicht nur innerhalb der Klassenhierarchie lässt sich der Zugriffstyp frei wählen, sondern auch innerhalb der Entity kann mit der Annotation @Accessdarüber entschieden werden. Dabei kann die Annotation @Access(PROPERTY)für eine Getter-Methode verwendet werden, wenn die Entity mit @Access(FIELD) annotiert ist. Der umgekehrte Fall für das Markieren von Instanzvariablen mit @Access(FIELD) ist analog.

i

Page 68: Jpa Mit en

Anforderungen an eine Entity

JPA mit Hibernate 67

Standardmäßig werden alle Attribute einer Entity ohne weiteres Zutun persistiert, es sei denn, diese sind mit der Annotation @Transient oder mit dem Java-Schlüsselwort transi-ent versehen.

4.1.1 Definition der Tabellen- und Spaltennamen

Entities werden in einer Tabelle gespeichert, die den gleichen Namen wie die Klasse hat. So wird beispielsweise die Entity User standardmäßig auf eine Tabelle User gemappt.

Mit der Annotation @Table kann man den Namen der Datenbanktabelle für das Mapping überschreiben sowie Schema und Katalog der Tabelle definieren.

Im obigen Beispiel werden die Entities User in die Tabelle T_User im Schema booksonlinegespeichert. Das Überschreiben der Default-Namen für Tabellen ist vor allem dann sinn-voll, wenn das Datenbankschema für die Anwendung bereits existiert. In JPA besteht dar-über hinaus auch die Möglichkeit, mit @SecondaryTable Entities zu mappen, deren Attribute über mehrere Datenbanktabellen verteilt sind. Bei mehr als zwei Tabellen für das Mapping der Entity werden die einzelnen @SecondaryTable Annotations mit @SecondaryTables grup-piert (Listing 4.2).

Neu in JPA 2.0 (Fortsetzung)

In Listing 4.1 wird die Verwendung der Annotation @Access konkret gezeigt:

@Entity @Access(FIELD) public class User { @Id Long id; @Access(PROPERTY) public String getLastname(){...}; ...

Listing 4.1: Verwendung von @Access

Die Verwendung von @Access(PROPERTY) auf Getter-Methoden oder von @Access(FIELD) auf Instanzvariablen ist nicht erlaubt.

@Entity@Table(name="T_User", schema="booksonline")public class User

@Entity@Table(name="T_User")@SecondaryTables({ @SecondaryTable(name="T_USER_DETAIL"),

Listing 4.2: Verwendung von @SecondaryTable

i

Page 69: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

68

Das Beispiel zeigt, wie für die Entity User zwei weitere Tabellen für das Mapping angege-ben werden. Dabei wird davon ausgegangen, dass in allen drei Tabellen der gleiche Pri-märschlüssel verwendet wird. Ist dies nicht der Fall, kann man mittels des Attributs pkJoinColumns der Annotation @SecondaryTable die abweichenden Primärschlüssel defi-nieren.

Die einfachen Attribute einer Klasse werden per Default in Datenbankspalten abgelegt, die den gleichen Namen wie das Attribut haben. Zu den einfachen Attributen zählen alle primitiven Java-Datentypen und deren Wrapper-Klassen sowie die Typen java.lang. String, java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.util.Calendar, java.sql.Date, java.sql.Time, java.sql.Timestamp, byte[], Byte[], char[] und Character[].

Mit der Annotation @Column können sowohl der Spaltename als auch weitere Meta-informationen für ein Attribut und dessen Mapping angegeben werden.

Im Beispiel werden für das Attribut email der Name der Datenbankspalte sowie dessen Länge definiert. Das Attribut wird auf die zweite Tabelle T_USER_DETAIL gemappt. Ist die Tabelle nicht als @SecondaryTable für die Entity angegeben, so wird ein entsprechender Fehler ausgegeben. Für das Attribut email wird mit Angabe von unique=true ein Unique-Constraint eingeführt. Weitere Informationen zur Annotation @Column befinden sich in der Referenz im Anhang.

Die Definition erweiterter Attribute wird in diesem Kapitel in den Abschnitten „Assozia-tionen“ und „Komponenten“ erläutert.

4.1.2 Erweiterungen der Entity mit Hibernate

Im Projekt „Hibernate Annotations“ sind zusätzliche Annotations definiert, die den Satz an Standardannotations von JPA erweitern, jedoch nicht ersetzen. Die Erweiterungen sollten nur eingesetzt werden, wenn zwingend erforderlich, da durch deren Einsatz die Portabilität der Anwendung auf andere Implementierungen von JPA verloren geht.

Mit der Annotation @org.hibernate.annotations.Entity können der Entity eine Reihe wei-terer Eigenschaften zugewiesen werden. In Listing 4.3 werden einige der Möglichkeiten gezeigt.

@SecondaryTable(name="T_USER_HIST")})public class User

public class User {@Column(name="user_email", length=100, table="T_USER_DETAIL", unique=true)private String email;

Listing 4.2: Verwendung von @SecondaryTable (Forts.)

Page 70: Jpa Mit en

Anforderungen an eine Entity

JPA mit Hibernate 69

Mit dem Parameter mutable=false wird spezifiziert, dass die Entity nicht veränderlich ist, nachdem sie in die Datenbank mittels insert eingefügt wurde. Danach wird jegliche Art von delete oder update ignoriert. Mit dynamicInsert und dynamicUpdate werden dynamisch Insert und Update-Statements generiert, je nachdem, welche Attribute gesetzt bzw. ver-ändert sind. Da das dynamische Generieren von Statements mehr Zeit und Ressourcen kostet, als die Wiederverwendung fest definierter Statements, sollten diese Einstellun-gen nur dann gewählt werden, wenn in der Tabelle große CLOB- und BLOB-Spalten vorhan-den sind, die sich voraussichtlich nur selten ändern. In diesem Fall können Ressourcen durch das Herausfallen der Spalten aus den Insert- und Update-Statements gespart wer-den. Mit dem Parameter optimisticLock wird die Strategie für das optimistische Locking (Kapitel 6) genauer definiert. Folgende Möglichkeiten stehen zur Auswahl:

� OptimisticLockType.VERSION: Die Default-Einstellung für das optimistische Locking, wobei ein Attribut der Entity als Version geführt wird.

� OptimisticLockType.NONE: Das optimistische Locking wird deaktiviert.

� OptimisticLockType.DIRTY: Es werden alle geänderten Attribute auf eventuell bereits vorhandene Änderungen in den entsprechenden Spalten der Datenbank überprüft.

� OptimisticLockType.ALL: Es werden alle, auch nicht geänderte, Attribute mit den Spal-ten der Datenbank auf Änderungen verglichen.

Die Änderung der Lock-Methode ist mit Bedacht durchzuführen, da durch die Verände-rungen starke Performanceeinbrüche entstehen können.

Mit der Annotation @org.hibernate.annotations.Where kann eine zusätzliche Bedingung für SQL-Abfragen definiert werden.

Alle Anfragen, die sich auf eine Entity vom Typ User beziehen, werden bei diesem Bei-spiel um and (user.aktiv='true') ergänzt. Eine vollständige Liste der für die Entity ver-fügbaren Annotation und deren Parameter befindet sich in der Referenz im Anhang.

@[email protected](mutable=false, dynamicInsert=true, dynamicUpdate=true, optimisticLock=OptimisticLockType.DIRTY)public class User

Listing 4.3: Erweiterte Möglichkeiten mit @org.hibernate.annotations.Entity

@org.hibernate.annotations.Where(clause="aktiv = ’true’")public class User

Page 71: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

70

4.2 PrimärschlüsselObjekte, die persistent in der Datenbank gespeichert sind, haben eine Datenbankiden-tität. Das heißt, dass zwei Objekte gleich sind, wenn sie in der selben Tabelle gespeichert sind und denselben Primärschlüssel haben. Das Verwalten der Primärschlüssel wird im Normalfall von Hibernate als Persistence Provider übernommen. Es besteht aber auch die Möglichkeit, vor dem Sichern eines Objekts den Primärschlüssel selbst zu setzen, wie es beispielsweise bei der Verwendung von natürlichen Schlüsseln nötig ist.

4.2.1 Anforderungen an den Primärschlüssel

In der Entity User wurde als Primärschlüssel der Typ Long gewählt. Grundsätzlich muss der gewählte Primärschlüssel folgende Anforderungen bestehen:

� Er darf nicht null sein

� Er kann nie verändert werden

� Er ist in einer Tabelle über alle Einträge eindeutig

Aus Sicht von JPA muss der Primärschlüssel der Entity mit der Annotation @Id markiert werden. Der Datentyp des Primärschlüssels – das markierte Attribut – kann ein primiti-ver Java-Datentyp, ein entsprechender Wrapper, java.lang.String, java.lang.Date oder java.sql.Date sein.

Es können auch zusammengesetzte Primärschlüssel verwendet werden, die mithilfe der Annotationen @EmbeddedId oder @IdClass festgelegt werden, um zum Beispiel natürliche Primärschlüssel abzubilden.

Natürliche Primärschlüssel sind Attribute, die in einer Businessanwendung eindeutig sind (Personalnummer, Steuernummer usw.) aber auch eine fachliche Information enthalten. Erfahrungen haben allerdings gezeigt, dass natürliche Schlüssel oft den oben genannten Anforderungen, vor allem nach längerer Laufzeit, nicht mehr entsprechen, und es dadurch zu Problemen kommen kann, die nur mit großem Aufwand gelöst werden können.

Neu in JPA 2.0

Auch java.math.BigDecimal und java.math.BigInteger können ab JPA 2.0 als Datentypen für Primärschlüssel verwendet werden. Jedoch sollte man die Sinnhaftigkeit einer Fließkommazahl als Primärschlüssel genau prüfen, bevor man sie als BigDecimal deklariert.

i

Page 72: Jpa Mit en

Primärschlüssel

JPA mit Hibernate 71

4.2.2 Datenbankidentität, Objektidentität und -gleichheit

Wie bereits erwähnt, bedeutet die Datenbankidentität, dass zwei Einträge in einer Tabelle gleich sind, wenn sie dieselbe ID haben. In der Welt von Java gibt es die Objekt-identität und die Objektgleichheit, die mit == und equals() überprüfbar sind.

� Objektidentität: Zwei Objekte sind gleich, wenn sie dieselbe Adresse im Speicher der Java VM (Virtual Machine) haben.

� Objektgleichheit: Zwei Objekt sind gleich, wenn sie denselben Inhalte haben und somit die Methode equals() true liefert. Falls equals() aus der Klasse java.lang.Objectnicht überschrieben wurde, wird die Objektidentität zurückgegeben.

Es wird empfohlen, equals() und hashcode() immer zu überschreiben. Insbesondere, wenn ein Objekt in einer Collection aufgenommen werden soll, sollten equals() und hashcode() überschrieben werden. Darüber hinaus sollte für die Objektgleichheit ein geeigneter Business-Key gefunden werden. Geeignet sind ein oder mehrere Eigenschaf-ten, die ein individuelles Objekt ausmachen. Bei der Klasse User wäre das beispielsweise die E-Mail-Adresse, da diese nicht null sein darf und eindeutig ist. Listing 4.4 zeigt die Implementierung von equals() und hashcode() für die Klasse User.

Das folgende Beispiel zeigt, dass der Primärschlüssel für die Objektgleichheit nicht geeignet ist. Angenommen, der User kann n Adressen haben und zwei Adressen (zum Beispiel Rechnungs- und Lieferadresse) werden hinzugefügt (Listing 4.5). Wenn die Objektgleichheit über den Primärschlüssel bestimmt würde, dann würde beim Hinzufü-gen der zweiten Adresse die erste überschrieben werden, da beide Adressen transient sind und noch den Primärschlüssel null haben. Dieses Problem kann allerdings nur im Zusammenhang mit Collections auftreten.

@Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof User)) return false; final User user = (User) obj; return !(email != null ? !email.equals(user.email) : user.email != null); }

@Override public int hashCode() { return 29 * (email != null ? email.hashCode() : 0); }

Listing 4.4: User.java (equals() und hashcode())

Page 73: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

72

4.2.3 Generatoren für den Primärschlüssel

Mit der Annotation @GeneratedValue kann ein ID-Generator gewählt werden. Die ID-Generatoren, die JPA bereitstellt, sind in Tabelle 4.1 aufgelistet.

Hibernate bietet darüber hinaus noch weitere ID-Generatoren an, die mit einer Hibernate-eigenen Annotation (@org.hibernate.annotations.GenericGenerator) verwendet werden können.

@GenericGenerator muss als Parameter einen eindeutigen Namen und eine Generatorstra-tegie haben. Je nach gewählter Generatorstrategie kann es weitere Parameter geben (Lis-ting 4.6).

...// Erste Adresse hinzufügen mit id = nulluser.addAddress(new Address(„street1“, „city1“));// id = null, überschreibt erste Adresseuser.addAddress(new Address(„street2“, „city2“));// Erzeugt nur eine neue Adresse für den Userem.merge(user);...

Listing 4.5: Probleme bei Verwendung des Primärschlüssels für die Objektgleichheit

Generatorname Beschreibung

AUTO Wählt entsprechend der darunterliegenden Datenbank eine Strategie (TABLE, IDENTIY, SEQUENCE). Entspricht der Hibernate-Generatorstrategie native (Tabelle 4.2).

TABLE Die IDs sind in einer eigenen Tabelle untergebracht. Entspricht der Hibernate-Genera-torstratgie hilo (Tabelle 4.2).

IDENTITY Der IDENTITY-Generator unterstützt Identity Columns, die es beispielsweise in MySQL, HSQLDB, DB2 und MS SQL Server gibt. Entspricht der Hibernate-Generatorstrategie identity (Tabelle 4.2).

SEQUENCE Der SEQUENCE-Generator unterstützt Sequences, die es beispielsweise in PostgreSQL, Oracle und Firebird gibt. Entspricht der Hibernate-Generatorstrategie sequence (Tabelle 4.2).

Tabelle 4.1: ID-Generatoren des JPA

@Id @GeneratedValue(generator="uuid-gen")@GenericGenerator(name="uuid-gen", strategy = "uuid")public String getId() { return this.id; }

Page 74: Jpa Mit en

Primärschlüssel

JPA mit Hibernate 73

Der Wert des Parameters name in @GenericGenerator muss derselbe sein wie der des Para-meters generator der Annotation @GeneratedValue. Ansonsten kann er frei gewählt wer-den. Die ID-Generatoren von Hibernate sind in Tabelle 4.2 erläutert.

@Id @GeneratedValue(generator="useridgen")@GenericGenerator(name="useridgen", strategy = "seqhilo", parameters = { @Parameter(name="max_lo", value = "5"), @Parameter(name="sequence", value="mysequence") })public Integer getId() { return this.id; }

Listing 4.6: Verwendung von @GenericGenerator

Strategie Parameter Beschreibung

native – Wählt entsprechend der darunterliegenden Datenbank eine Strategie (iden-tity, sequence oder hilo). Welche Strategie für eine Datenbank gewählt wird, ist in den Dialekten definiert.

uuid separator Gibt einen String mit Länge von 32 ausschließlich hexadezimalen Zeichen zurück. Optional kann ein Separator zwischen den UUID-Komponenten mit generiert werden.

hilo table columnmax_lo

Dieser Generator nutzt einen Hi/Lo-Algorithmus, um numerische IDs (Long, long, int, ...) zu erzeugen. Optional können die Spaltennamen für die Hi/Lo-Tabelle angegeben werden (Default-Werte: hibernate_unique_key und next_hi). Mit max_lo kann die Anzahl der IDs bestimmt werden, die erzeugt werden, bevor wieder ein Datenbankzugriff erfolgt, um den Max-Value zu erhöhen. Der Generator kann nicht mit einer eigenen Connection oder eine über JTA (Java Transaction API) erhaltene Connection verwendet werden, da Hibernate in der Lage sein muss, den „hi“-Value in einer neuen Transaktion zu holen.

seqhilo sequencemax_loparameters

Dieser Generator kombiniert einen Hi/Lo-Algorithmus mit einer darunterliegenden Sequence, die die Hi-Values generiert. Die Datenbank muss Sequences unterstüzen, z. B. Oracle und PostgreSQL. Der Parameter parame-ters wird dem Create Sequence Statement hinzugefügt, beispielsweise INCRE-MENT BY 1 START WITH 1 MAXVALUE 100 NOCACHE. Mit sequence kann ein Name für die Sequence vergeben werden, Default ist hibernate_sequence.

identity – Dieser Generator unterstützt Identity Columns bzw. autoincrement, die es beispielsweise in MySQL, HSQLDB, DB2 und MS SQL Server gibt.

select key (required)

Die ID wird über ein Select mit einem eindeutigen key erhalten. Der Primärschlüssel wird von der Datenbank, zum Beispiel mit einem Trigger, ver-geben.

Tabelle 4.2: Zusätzliche ID-Generatoren von Hibernate

Page 75: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

74

Zusätzlich zu den bereits eingebauten Generatoren können weitere selbst erstellt wer-den. Dazu muss das Interface IdentifierGenerator implementiert werden.

4.3 KomponentenKomponenten werden oftmals, beispielsweise in Hibernate, auch als Value-Typen bezeichnet. Es muss zwischen Entity- und Value-Typen unterschieden werden. Entities haben einen Primärschlüssel und einen Lebenszyklus. Value-Typen hingegen haben keine Datenbankidentität, das heißt, sie haben keinen Primärschlüssel. Sie gehören zu einer Entity, und ihr Zustand wird innerhalb der Tabelle der dazugehörigen Entity gesi-chert. Ausnahmen bilden hier Collections von Value-Typen, die am Ende des Kapitels behandelt werden.

Die Klasse User im Beispielprojekt enthält noch keine Informationen über die Anschrift eines Benutzers. Dazu soll es die Klasse Address geben, die als Komponente an den Userangebunden werden soll, wie es in Abbildung 4.1 zu sehen ist. Normalerweise wird eine Tabelle auf eine Klasse abgebildet. In der objektorientieren Programmierung ist es jedoch oft wünschenswert, eine Tabelle auf mehrere Klassen abzubilden, um damit ein feingra-nulares Klassenmodell entwerfen zu können. Komponenten ermöglichen die Abbildung mehrerer Klassen auf eine Tabelle.

sequence sequenceparameters

Dieser Generator unterstützt Sequences, die es beispielsweise in PostgreSQL, Oracle und Firebird gibt. Die ID kann vom Type Long, long, int etc. sein. Der Parameter parameters wird dem Create Sequence Statement hinzugefügt, beispielsweise INCREMENT BY 1 START WITH 1 MAXVALUE 100 NOCACHE. Mit sequence kann ein Name für die Sequence vergeben werden, Default ist hibernate_sequence.

assigned – Bei assigned muss die ID selbst gesetzt werden, vor dem Aufruf von save() (Session) oder persist() (EntityManager). Nützlich bei natürlichen Keys. Das ist zugleich auch die Default-Strategie, falls die Annotation @GeneratedValue nicht angegeben wurde.

increment – Der maximale Primärschlüsselwert einer Tabelle wird beim Start der Anwen-dung gelesen und dann jedes Mal erhöht, wenn ein Insert erfolgt. Sollte nicht in einer Cluster-Umgebung benutzt werden.

foreign property (required)

Benutzt die ID eines assoziierten Objekts, das beim Parameter property ange-geben werden muss. Wird gewöhnlich in Verbindung mit 1-zu-1-Beziehungen verwendet.

guid – Nutzt die von MS SQL Server oder MySQL generierte GUID.

Strategie Parameter Beschreibung

Tabelle 4.2: Zusätzliche ID-Generatoren von Hibernate (Forts.)

Page 76: Jpa Mit en

Komponenten

JPA mit Hibernate 75

Abbildung 4.1: Beziehung „User“ zu „Address“

Die Abbildung zeigt eine Komposition zwischen den Objekten User und Address. Eine Komposition ist eine strengere Form der Aggregation. Die Lebensdauer eines Objekts Address ist dabei an das Objekt User gebunden, eine Address wird also immer mit oder nach dem User erzeugt und vor oder mit dem User zerstört. Die Komposition wird in der UML1 mit einer gefüllten Raute dargestellt, die Aggregation mit einer leeren Raute. Die Aggregation beschreibt eine schwache Beziehung zwischen Objekten. Das Objekt ist ein Teil eines anderen Objekts, kann aber im Gegensatz zur Komposition auch alleine existie-ren. In der Implementierung mit Java macht das keinen Unterschied, aber das Objekt Address ist in diesem Fall für den Persistence Provider Hibernate ein Value-Typ und hat keinen eindeutigen Bezeichner (Primärschlüssel).

In Listing 4.7 ist die Implementierung mit Annotationen der Klasse Address zu sehen. Der Klasse User muss dann noch die Komponente Address hinzugefügt werden (Listing 4.8).

1. Unified Modeling Language: http://www.omg.org

package booksonline.bo;import javax.persistence.Embeddable;

@Embeddablepublic class Address { private String street; private String city; public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; }}

Listing 4.7: Klasse Address.java

@Entity@Table(name = "T_USER")public class User { private Long id; @Embedded private Address address;

Listing 4.8: Klasse User.java

User-firstname: String-lastname: String-email: String

Address-street: String-city: String

Page 77: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

76

Mit der Annotation @Embeddable wird eine Komponente auf Klassenebene definiert. Die Entity User mit dem Attribut address muss nicht in irgendeiner Weise gekennzeichnet werden, die Angabe von @Embedded ist optional.

Nun kann das Beispiel aus dem dritten Kapitel, in dem der User gesichert wurde, um die Adresse erweitert werden. Das erweiterte Beispiel befindet sich in Listing 4.9.

Auf der Entity User wird der Setter setAddress() mit Address als Parameter aufgerufen. Die Komponente Address ist ein Teil der Entity User. Um den User persistent mit der Address in der Datenbank zu sichern, muss lediglich entityManger.persist(), enthalten in der Methode userDao.persist(), für den User aufgerufen werden. Nach erfolgreichem Ablauf des Beispiels ist der User mit seiner Anschrift in der Datenbank enthalten. Tabelle 4.3 zeigt Ihnen den Eintrag in der Datenbank.

... public Address getAddress() { return address; }

public void setAddress(Address address) { this.address = address; } ...}

Neu in JPA 2.0

Komponenen können ab JPA 2.0 auch selbst wieder Komponenten enhalten, die ebenso mit @Embeddable annotiert werden. Des Weiteren können Kompo-nenten auch Collections von Basistypen oder Komponenten enthalten. Dane-ben gibt es die Möglichkeit, aus Komponenten heraus Entities und Collections von Entities zu referenzieren. Dabei ist allerdings zu beachten, dass die Bezie-hung zwischen der referenzierten Entity und jener Entity besteht, die die Komponente enthält, da Komponenten keine eigene Identität besitzen.

... user = new User("Moritz", "Muster", "[email protected]"); user.setAddress(new Address("Musterstr. 11", "Musterhausen")); userDao.persist(user);...

Listing 4.9: SaveUserAddress.java

Id Firstname Lastname Email Address_Street Address_City

1 Moritz Muster booksonline@... Musterstr. 11 Musterhausen

Tabelle 4.3: Datenbanktabelle T_USER

Listing 4.8: Klasse User.java (Forts.)

i

Page 78: Jpa Mit en

Komponenten

JPA mit Hibernate 77

Als Spaltennamen für die Komponenten werden per Default, ebenso wie bei den Enti-ties, die Attributnamen verwendet. Wenn die Eigenschaften, wie beispielsweise der Name oder die Länge der Spalte, angepasst werden sollen, kann die Annotation @Columnin der Klasse Address verwendet werden (Listing 4.10).

Zusätzlich wurde die Spalte address_city auf 50 Zeichen begrenzt. Alle Eigenschaften der Annotation @Column befinden sich im Anhang.

Mit den Annotationen @AttributeOverrides und @AttributeOverride können die Attribute der Komponente von der Entity aus überschrieben werden. Das obige Beispiel würde dann wie in Listing 4.11 aussehen.

Mit @AttributeOverrides und @AttributOverride können somit Komponenten an die Bedürfnisse einer jeweiligen Entity angepasst werden, und die Wiederverwendung der Komponente ist ebenfalls sichergestellt.

@Column(name = "address_street") public String getStreet() { return street; } public void setStreet(String street) { this.street = street; }

@Column(name = "address_city", length = 50) public String getCity() { return city; } public void setCity(String city) { this.city = city; }

Listing 4.10: Überschreiben der Spaltennamen mit @Column

@Entity@Table(name = "T_USER")public class User { ... @Embedded @AttributeOverrides({ @AttributeOverride( name = "street", column = @Column(name = "userstreet")), @AttributeOverride( name = "city", column = @Column(name = "usercity", length = 50))}) public Address getAddress() { return address; } ...}

Listing 4.11: Attribute überschreiben mit @AttributeOverrides

Page 79: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

78

4.4 AssoziationenEine Assoziation ist die Verbindung mindestens zweier Entities und erlaubt das Navi-gieren von der einen zur anderen Entity. In unserem Beispielprojekt haben wir eine Asso-ziation zwischen Book und User. Falls ein Buch ausgeliehen ist, kann von Book zu User mitder Methode getUser() navigiert werden. Im häufigsten Fall handelt es sich um einebinäre Assoziation, das heißt eine Beziehung zwischen zwei Entities. Von einer reflexivenAssoziation spricht man, wenn die Entity die Beziehung zu sich selbst hat. In Abbildung4.2 sieht man eine binäre und eine reflexive Assoziation.

Abbildung 4.2: Binäre und reflexive Assoziation

Die Kardinalität beschreibt den Grad einer Beziehung zwischen zwei Entities. Es wirdzwischen folgenden Kardinalitäten unterschieden:

� Eins-zu-Eins (1:1): Eine Entity steht mit einer anderen Entity in Beziehung. Für dieandere Richtung gilt dasselbe.

� Eins-zu-Viele/Viele-zu-Eins (1:n/n:1): Eine Entity steht mit mehreren Entities einesTyps in Beziehung. In die andere Richtung steht eine Entity mit einer Entity des ande-ren Typs in Beziehung.

� Viele-zu-Viele (n:n): Wie 1:n, nur dass nun auch in die andere Richtung eine Entity mitmehreren Entities eines Typs in Beziehung steht.

Bei allen Assoziationen wird noch zwischen unidirektionalen und bidirektionalen Bezie-hungen unterschieden. Unidirektional bedeutet, dass bspw. von einer Klasse Person zurAdresse navigiert werden kann, nicht aber von Adresse zur Person. Bei einer bidirektiona-len Beziehung kann auch von der Klasse Adresse zur Person navigiert werden.

In den folgenden Abschnitten wird erläutert, wie diese Beziehungen mit JPA beschriebenwerden können.

4.4.1 1-zu-1-Beziehungen

1-zu-1-Beziehungen lassen sich auf zwei Arten umsetzen. Zum einen können die inBeziehung stehenden Entities immer denselben Primärschlüssel haben oder es wird einFremdschlüssel verwendet, mit der Einschränkung, dass dieser eindeutig sein muss. Umdie beiden Möglichkeiten zu zeigen, wird die Beziehung zwischen User und Address ver-wendet. Es ist zu beachten, dass in diesem Fall Address eine Entity ist und daher eine IDbenötigt. Im Beispielcode werden dafür neue Klassen verwendet, um die unterschied-lichen Arten der 1-zu-1-Beziehungen vergleichend zu zeigen.

Page 80: Jpa Mit en

Assoziationen

JPA mit Hibernate 79

Eine 1-zu-1-Beziehung wird mit der Annotation @OneToOne markiert (Listing 4.12).

Ob die Assoziation uni- oder bidirektional ist, hängt davon ab, ob in der Klasse Adress-Default eine Referenz auf User existiert. Bei einer bidirektionalen Beziehung kann nur eine Entity der Besitzer der Assoziation sein. Für diese Entity wird entsprechend das Attribut in der Datenbank gespeichert. Die gegenüberliegende Seite nutzt lediglich die Information, um die Referenz darzustellen. In Listing 4.13 wird gezeigt, wie eine entspre-chende Referenz mit der Annotation @OneToOne markiert werden muss. Dabei wird mit dem Parameter mappedBy auf das Attribut der zu referenzierenden Entity verweisen.

Die 1-zu-1-Beziehung wird in JPA per Default über einen Fremdschlüssel in der Daten-bank abgebildet. Dabei werden für die Spalten- und Tabellen in der Datenbank die fol-genden Festlegungen getroffen:

� Die Entity User wird auf eine Tabelle mit Namen USER gemappt, sofern dies nicht durch die Annotation @Table überschrieben wird.

� Die Entity AdressDefault wird auf eine Tabelle mit Namen ADRESSDEFAULT gemappt.

� In der Tabelle USER wird ein Fremdschlüssel auf die Tabelle ADRESSDEFAULT angelegt. Der Name der Fremdschlüsselspalte wird aus dem Namen der zu referenzierenden Tabelle und dem Spaltennamen des Primärschlüssels, verbunden mit einem Unter-strich, zusammengesetzt. In diesem Fall wird in der Entity User eine Spalte mit Namen ADRESSDEFAULT_ID angelegt.

@Entitypublic class User { ...

@OneToOne private AddressDefault addressDefault; ...}

Listing 4.12: 1-zu-1-Beziehung in Entity „User“

@Entitypublic class AddressDefault implements Serializable { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; ... @OneToOne(mappedBy="addressDefault") private User user; ...}

Listing 4.13: Bidirektionale Beziehung in Entity „AddressDefault“

Page 81: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

80

� Der Datentyp der Fremdschlüsselspalte ist derselbe wie der des Primärschlüssels von ADDRESDEFAULT

� Auf der Fremdschlüsselspalte wird ein unique-Constraint angelegt.

Der Test in Listing 4.14 speichert einen User und seine zugehörige Adresse. Zwischen User und AdressDefault besteht in diesem Beispiel eine bidirektionale Beziehung. Für das korrekte Setzen der Referenzen ist der Programmierer verantwortlich. Da beide Entities unabhängig voneinander existieren können, müssen sie auch separat mit persist()gespeichert werden.

Um nicht jede Entity eines Objektgraphen einzeln speichern zu müssen, können mittels des Parameters cascade die Kaskadierungseigenschaften für Speichern, Update und Löschen definiert werden. In Listing 4.15 sind die dafür benötigten Änderungen dargestellt.

Mit der Einstellung CascadeType.PERSIST wird das Attribut addressDefault beim Speichern automatisch mit persistiert, sodass lediglich nur noch persist(user) aufgerufen werden muss. Der Aufruf von persist(address) in Listing 4.14 kann entfallen. Näheres zu den weiteren Möglichkeiten für die Kaskadierung findet sich in Kapitel 4.4.4.

@Test public void testUserWithAddressDefault() { EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user = new User("Hans", "Hase", "[email protected]"); AddressDefault address = new AddressDefault("Laubweg 42", "Hasenhausen"); user.setAddressDefault(address); address.setUser(user); em.persist(user); em.persist(address); tx.commit(); em.close(); }

Listing 4.14: Test zum Speichern der bidirektionalen Beziehung zwischen User und Address

@Entitypublic class User implements Serializable {... @OneToOne(cascade=CascadeType.PERSIST) private AddressDefault addressDefault;...

Listing 4.15: Kaskadierungsoption für die referenzierte Entity

Page 82: Jpa Mit en

Assoziationen

JPA mit Hibernate 81

Mit dem Parameter optional kann in der Annotation angegeben werden, ob die Bezie-hung gefüllt sein muss oder nicht. Will man also das Setzen der Beziehung zur Pflicht machen, kann man die Annotation wie folgt erweitern:

Es empfiehlt sich, die Kaskadierungsoption CascadeType.PERSIST anzugeben, da es sonst beim Speichern des Users oder der Adresse zu einem Fehler kommen kann, wenn die als Pflicht angegebene Referenz noch nicht persistiert wurde.

Mapping mittels Primärschlüssel

Eine echte 1-zu-1-Beziehung wird über dieselben Primärschlüssel erreicht. In Listing 4.16werden die Entities User und AddressPKey zu einer solchen 1-zu-1-Beziehung verbunden. Die Annotation @OneToOne kennzeichnet AddressPKey, wie im vorherigen Beispiel, als 1-zu-1-Beziehung, und mit der Annotation @PrimaryKeyJoinColumn geht der objektrelationale Mapper davon aus, dass die beiden in Beziehung stehenden Objekte immer denselben Primärschlüssel haben.

@OneToOne(optional=false, cascade=CascadeType.PERSIST)

@Entity@Table(name = "T_USER")public class User { ... @Id @GeneratedValue public Long getId() { return id; } private void setId(Long id) { this.id = id; }

@OneToOne @PrimaryKeyJoinColumn public Address getAddress() { return address; } ...}

@Entity@Table(name = "T_ADDRESS")public class Address { private Long id; ... @Id public Long getId() { return id; } ...}

Listing 4.16: Unidirektionale 1-zu-1-Beziehung mit gemeinsamem Primärschlüssel

Page 83: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

82

Um aus der hier gezeigten unidirektionalen Beziehung eine bidirektionale zu machen, wird, analog zum ersten Beispiel in diesem Abschnitt, eine Referenz auf User in der Klasse AddressPKey benötigt. Diese muss ebenso mit @OneToOne markiert werden.

Wie man am dazugehörigen Test in Listing 4.17 sehen kann, ist man mit dieser Lösung verpflichtet, den Primärschlüssel für die Entity AddressPKey selbst zu setzen. Dazu muss erst em.persist(user)aufgerufen werden, um einen Primärschlüssel für den User zu erhal-ten. Danach muss Address selbst nochmals mit em.persist(address) gespeichert werden.

foreign-Generatorstrategie mit Hibernate

Hibernate bietet mit der Generatorstrategie foreign2 noch eine weitere Möglichkeit, eine 1-zu-1-Beziehung mit gemeinsamem Primärschlüssel zu erzeugen. Bei dieser 1-zu-1-Beziehung kümmert sich Hibernate darum, dass beide Entities denselben Primärschlüs-sel haben. Der Primärschlüssel muss also nicht selbst gesetzt werden. Die Generatorstra-tegie foreign wird in der Entity Login benutzt. Sie benötigt einen Parameter property, das ist in unserem Fall user. Der Primärschlüssel in der Entity User wird mit der Default-Generator-Strategie generiert. Die daraus resultierende Klasse Login sieht man in Listing 4.18. Für eine bidirektionale Beziehung sind die Änderungen analog zur obigen 1-zu-1-Beziehung mit gemeinsamem Primärschlüssel.

@Testpublic void testUserWithAddressPKey() { EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user = new User("Hans", "Hase", "[email protected]"); AddressPKey address = new AddressPKey("Laubweg 42", "Hasenhausen"); user.setAddressPKey(address); em.persist(user); address.setUser(user); address.setId(user.getId()); em.persist(address); tx.commit(); em.close();}

Listing 4.17: Test für 1-zu-1-Beziehung mit selbem Primärschlüssel

2. Generatorstrategien: Kapitel 4.2

@Entitypublic class Login implements Serializable { @Id @GeneratedValue(generator = "foreign")

Listing 4.18: Unidirektionale 1-zu-1-Beziehung mit gemeinsamem Primärschlüssel und Hibernate-Style

Page 84: Jpa Mit en

Assoziationen

JPA mit Hibernate 83

Der Vorteil des „Hibernate-Styles“ ist, dass man sich mit der Generatorstrategie foreignnicht um die Primärschlüssel kümmern muss. In Listing 4.19 wird ein entsprechender Test gezeigt. Die id in User braucht nun nicht mehr gesetzt zu werden, jedoch muss man der Entity Login vor dem Speichern mit persist() den User zuweisen. Auf User muss persist() danach nicht mehr aufgerufen werden.

4.4.2 1-zu-n- und n-zu-1-Beziehungen

Im Beispielprojekt hat jedes Buch einen Verlag und umgekehrt kann ein Verlag kein, eins oder mehrere Bücher haben. In diesem Fall existiert eine bidirektionale 1-zu-n- bzw. n-zu-1-Beziehung zwischen Book und Publisher. In Listing 4.20 ist die Implementierung der Klasse Book zu sehen.

@GenericGenerator(name = "foreign", strategy = "foreign", parameters = { @Parameter(name = "property", value = "user") } ) private Long id; ... @OneToOne @PrimaryKeyJoinColumn private User user; ...}

@Testpublic void testLoginWithHibernatePKey() { EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user = new User("Max", "Muster", "[email protected]"); Login login = new Login(user, "max.muster", "geheim"); em.persist(login); tx.commit(); em.close();}

Listing 4.19: Test für unidirektionale 1-zu-1-Beziehung mit gemeinsamem Primärschlüssel und Hibernate-Style

Listing 4.18: Unidirektionale 1-zu-1-Beziehung mit gemeinsamem Primärschlüssel und Hibernate-Style (Forts.)

Page 85: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

84

Die Klasse Publisher muss ebenfalls erstellt werden. Sie ist eine Entity und benötigt mindestens die Annotation @Entity und einen Primärschlüssel, gekennzeichnet mit der Annotation @Id. Die 1-zu-n-Beziehung zu Book bilden wir mit der Annotation @OneTo-Many. Mit dem Attribut mappedBy wird wieder der Besitzer der Beziehung bestimmt.

Bei einer 1-zu-n Beziehung, bei der mittels Fremdschlüssel gemappt wird, ist immer die n-Seite der Besitzer der Assoziation (Listing 4.21).

Um sicherzustellen, dass auch die Rückreferenzen korrekt gesetzt werden, sollte fol-gende Methode in der Klasse Publisher nicht fehlen:

@Entitypublic class Book {@Id@GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String ISBN; @ManyToOne private Publisher publisher; ...}

Listing 4.20: Book mit n-zu-1-Beziehung zu Publisher

@Entitypublic class Publisher { @Id @GeneratedValue private Long id; @OneToMany(mappedBy = "publisher") private Set<Book> books = new HashSet<Book>(); ...}

Listing 4.21: Publisher mit 1-zu-n-Beziehung zu Book

public void addBook(Book book) { this.books.add(book); book.setPublisher(this);}

Page 86: Jpa Mit en

Assoziationen

JPA mit Hibernate 85

Die Annotation @JoinColumn erlaubt die Definition des Fremdschlüssels der Beziehung. In Listing 4.22 wird zum einen bestimmt, dass der Fremdschlüssel nie null sein darf, zum anderen wurde der Name des Fremdschlüssels definiert. Falls kein Name für den Fremdschlüssel angegeben wurde, wird er aus dem Namen des Beziehungsfelds (hier: publisher) und dem Primärschlüsselfeld, getrennt mit einem Unterstrich, zusammenge-setzt. In unserem Fall würde die Fremdschlüsselspalte in der Tabelle PUBLISHER den Namen publisher_id haben. Man kann aber auch, wie hier geschehen, den Namen der Primärschlüsselspalte einer Entity selbst definieren, dann würde der Name aus dem Beziehungsfeld und dem definierten Primärschlüsselfeld zusammengesetzt.

Der Fremdschlüssel hat nun den Namen publisher_myId. Das ID-Feld in Publisher muss allerdings ebenfalls angepasst werden. Entweder es wird in myId umbenannt, oder das Mapping wird mittels der Annotation @Column(name=“myId“) verändert. Im Anhang befin-den sich weitere Eigenschaften der Annotation @JoinColumn.

Die Angabe von @JoinColumn ist optional. Um eine n-zu-1-Beziehung zu beschreiben, muss mindestens die Annotation @ManyToOne vorhanden sein. Die Attribute von @Many-ToOne können die Performance erheblich beeinflussen, es sollte also vorsichtig damit umgegangen werden.

Da wäre beispielsweise cascade, damit lässt sich bestimmen, ob ein merge(Object entity), persist(Object entity) usw. an die anhängenden Objekte durchgereicht wird oder nicht. Das Default-Verhalten ist kein Durchreichen. Mehr dazu in Kapitel 4.4.4. Des Weiteren kann die Fetch-Strategie bestimmt werden. Standardmäßig ist das Verhalten EAGER, das

Neu in JPA 2.0

Für die Annotationen @OneToOne und @OneToMany steht ein neues Attribut or-phanRemoval zur Verfügung. Setzt man das Attribut auf true, so werden beim Löschen der Entity auf der 1-Seite der Beziehung automatisch auch die refe-renzierten Entites gelöscht. Dies ist vor allem dann sinnvoll, wenn die Entity der 1-Seite als „Besitzer“ der Beziehung vorgesehen ist und demzufolge die referenzierten Entities ohne den „Besitzer“ keinen Sinn machen. Allerdings funktioniert dies nur mit Entities, die durch den EntityManager verwaltet werden und somit nicht detached sind.

@ManyToOne@JoinColumn(referencedColumnName = "myId", nullable=false)public Publisher getPublisher() { return publisher;}

Listing 4.22: Verwendung von @JoinColumn

@ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY) public Publisher getPublisher() { return publisher; }

i

Page 87: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

86

heißt, beim Laden des Objekts werden die Beziehungsobjekte sofort mitgeladen, was natürlich den Zugriff auf die Datenbank verlängert. Die Eigenschaft fetch kann auch auf LAZY gestellt werden, dann wird erst beim Zugriff auf die Beziehung das entsprechende Objekt nachgeladen. Näheres dazu in Kapitel 8.

Beziehungen mit Verbindungstabelle

Für die 1-zu-n- und/oder n-zu-1-Beziehung kann auch eine Verbindungstabelle verwen-det werden. Dies ist dann sinnvoll, wenn beispielsweise eine unidirektionale 1-zu-n-Beziehung benötigt wird.

Dafür wird die Annotation @JoinTable benötigt. In Listing 4.23 sieht man die entspre-chend erweiterte Klasse Publisher und die neue Klasse BookJoinTable. Es werden in die-sem Fall drei Tabellen generiert:

� BOOKJOINTABLE mit Name, ISBN etc.

� PUBLISHER

� BOOKJOINTABLE_PUBLISHER mit zwei Fremdschlüsseln auf BOOKJOINTABLE.ID und PUBLISHER.ID

Die Join-Tabelle wird per Default aus den Namen der beiden Tabellen, die in Beziehung stehen, zusammengesetzt. Der Name der Verbindungstabelle kann aber auch über die Annotation @JoinTable bestimmt werden. Die Namen der Fremdschlüsselspalten in der Verbindungstabelle können ebenfalls definiert werden, dazu gibt es die Attribute join-Columns und inverseJoinColumns.

Neu in JPA 2.0

Die Definition eines Fremdschlüssels mit @JoinColumn kann ab JPA 2.0 auch auf eine unidirektionale 1-zu-n-Beziehung angewendet werden. Dadurch enthält die n-Seite der Beziehung eine Spalte in der Datenbanktabelle, auf die die dazugehörige Entity jedoch nicht zugreifen kann. Dadurch wird die Verbin-dungstabelle „gespart“, die in JPA 1.0 angelegt werden musste, um diesen Beziehungstyp abzubilden.

Neu in JPA 2.0

Die Verwendung der Annotation @JoinTable zum Erzeugen einer Verbindungs-tabelle kann ab JPA 2.0 auf alle Beziehungstypen angewandt werden. Somit kann man bspw. auch für eine unidirektional 1-zu-1-Beziehung eine Verbin-dungstabelle verwenden. Allerdings sollte man die Sinnhaftigkeit einer sol-chen Entscheidung genauestens von Fall zu Fall überprüfen, denn nicht alles, was erlaubt ist, ist auch wirklich immer sinnvoll.

i

i

Page 88: Jpa Mit en

Assoziationen

JPA mit Hibernate 87

4.4.3 N-zu-m-Beziehungen

N-zu-m-Beziehungen werden immer mit einer Verbindungstabelle (Assoziationstabelle) gebildet. Im Beispielprojekt wird zwischen Book und BookCategory eine M-zu-n-Bezie-hung hergestellt, die mit der Annotation @ManyToMany des JPA sehr einfach erstellt werden kann. In Listing 4.24 und Listing 4.25 sind die entsprechenden Codeausschnitte von Book-Category und Book zu sehen.

Auf der Seite der Entity BookCategory definieren wir wiederum die „Besitzer“-Seite der bidirektionalen Beziehung.

@Entitypublic class JoinTableBook implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String title; private String ISBN; private String author; ...}@Entitypublic class Publisher implements Serializable { ... @OneToMany(cascade=CascadeType.PERSIST) private Set<JoinTableBook> joinTableBooks; ...}

Listing 4.23: n-zu-1 Beziehung mit Verbindungstabelle

@Entitypublic class Book { ... @ManyToMany private Set<BookCategory> bookCategories;}

Listing 4.24: Erweiterungen der Entity „Book“

@Entitypublic class BookCategory implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO)

Listing 4.25: Die Entity „BookCategory“

Page 89: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

88

Auch hier sollte eine add-Methode hinzugefügt werden, um sicherzustellen, dass beide Seiten der Beziehung gesetzt werden:

Die aus der obigen N-zu-m-Beziehung zwischen Book und BookCategory resultierenden Tabellen sieht man in Abbildung 4.3.

Abbildung 4.3: Tabellen der N-zu-m-Beziehung zwischen Book und BookCategory

Die Verbindungstabelle wird aus den Namen der zu verbindenden Entities, getrennt durch einen Unterstrich zusammengesetzt. Dabei wird der Besitzer der Assoziation zuerst angegeben. Die Fremdschlüsselspalten werden analog zu denen der 1-zu-n-Bezie-hungen benannt: Attributname und Name der referenzierten Primärschlüsselspalte, ver-bunden mit Unterstrich.

4.4.4 Transitive Persistenz

Transitive Persistenz erlaubt die Weitergabe von Entity-Operationen wie beispielsweise persist(), merge() und delete() auf in Beziehung stehende Entities. Angenommen, ein Objekt A hat eine 1-zu-n-Beziehung zu einem Objekt B. Falls nun auf Objekt A persist()aufgerufen wird, erfolgen standardmäßig keine Aufrufe von persist() auf die Objekte B, die an diesem Objekt A hängen. Die Annotationen @OneToOne, @OneToMany, @ManyToOne und @ManyToMany erlauben das Durchreichen der Entity-Operationen, wie zum Beispiel per-sist(). Wenn nun bei der Beziehung Objekt A zu Objekt B

definiert ist, würden bei einem Aufruf von persist() auf Objekt A automatisch auch alle an A hängenden transienten Objekte B persistent in die Datenbank geschrieben.

private Long id; private String description; @ManyToMany(mappedBy = "bookCategories") private Set<Book> books; ...}

public void addBookCategory(BookCategory bookCategory) { this.bookCategories.add(bookCategory); bookCategory.getBooks().add(this);}

@OneToMany(casacade = CascadeType.PERSIST)

Listing 4.25: Die Entity „BookCategory“ (Forts.)

<<Table>>book

<<PK>> id ...

<<Table>>book_bookcategory

<<FK>><<PK>> book_id <<FK>><<PK>> bookcategories_id

<<Table>>bookcategory <<PK>> id ...

Page 90: Jpa Mit en

Assoziationen

JPA mit Hibernate 89

In Tabelle 4.4 findet man alle CascadeTypes des JPA.

Darüber hinaus bietet Hibernate weitere CascadeTypes, die die CascadeTypes des JPA über-schreiben und erweitern. Dazu muss die Annotation @org.hibernate.annotations.Cascadeverwendet werden (siehe Listing 4.26) um dann mit der Annotation @org.hibernate.annota-tions.CascadeType die gewünschten CascadeTypes zu benennen.

In Tabelle 4.5 werden alle möglichen CascadeTypes von Hibernate beschrieben.

javax.persistence. CascadeType

Beschreibung

PERSIST Ein Aufruf von persist(Object entity) wird an die in Beziehung stehenden Objekte mit diesem CascadeType durchgereicht.

MERGE Ein Aufruf von merge(Object entity) wird an die in Beziehung stehenden Objekte mit diesem CascadeType durchgereicht.

REMOVE Ein Aufruf von remove(Object entity) wird an die in Beziehung stehenden Objekte mit diesem CascadeType durchgereicht.

REFRESH Ein Aufruf von refresh(Object entity) wird an die in Beziehung stehenden Objekte mit diesem CascadeType durchgereicht.

ALL Enspricht cascade = { PERSIST, MERGE, REMOVE, REFRESH }.

Tabelle 4.4: Übersicht der CascadeTypes des JPA

@[email protected]( { @org.hibernate.annotations.CascadeType.SAVE_UPDATE, @org.hibernate.annotations.CascadeType.MERGE } )public List<B> getB() { return b; }

Listing 4.26: Verwendung von @org.hibernate.annotations.Cascade

org.hibernate.annotations. CascadeType

Beschreibung

PERSIST Entspricht javax.persistence.CascadeType.PERSIST.

MERGE Entspricht javax.persistence.CascadeType.MERGE.

REMOVE Entspricht javax.persistence.CascadeType.REMOVE.

REFRESH Entspricht javax.persistence.CascadeType.REFRESH.

DELETE Entspricht javax.persistence.CascadeType.REMOVE.

SAVE_UPDATE Ein Aufruf von save(Object entity) oder update(Object entity) auf der Hibernate Session wird an die in Beziehung stehenden Objekte mit diesem CascadeType durchgereicht.

Tabelle 4.5: Hibernate CascadeType Annotations

Page 91: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

90

Abschließend soll das folgende Beispiel die transitive Persistenz verdeutlichen. In Listing 4.27 sind Ausschnitte aus den Klassen Publisher und Book zu sehen. Es wird der Cascade-Type.PERSIST und der CascadeType.MERGE für die 1-zu-n-Beziehung zwischen Publisherund Book definiert. Der darauffolgende Test in Listing 4.28 zeigt, dass kein Aufruf von persist(Object entity) oder merge(Object entity) auf Book nötig ist.

REPLICATE Ein Aufruf von replicate(Object entity, ...) (Objekt wird unter Verwen-dung der existierenden ID in der Datenbank abgelegt) wird an die in Beziehung stehenden Objekte mit diesem CascadeType durchgereicht.

DELETE_ORPHAN Alle persistenten Entities, die aus einer Beziehung zu einer persistenten Entity, beispielsweise aus einer Collection, genommen wurden (Objekte, die nicht mehr referenziert werden), werden automatisch gelöscht.

LOCK Ein Aufruf von lock(Object entity, ...) (Objekt wird mit angegebenem LockMode versehen, um beispielsweise Versionschecks oder pessimistisches Locking durchzuführen) wird an die in Beziehung stehenden Objekte mit die-sem CascadeType durchgereicht.

EVICT Ein Aufruf von evict(Object entity) (Objekt wird aus der Session gelöscht und damit verhindert, dass Änderungen in die Datenbank geschrieben wer-den) wird an die in Beziehung stehenden Objekte mit diesem CascadeType durchgereicht.

ALL Beinhaltet SAVE_UPDATE, DELETE, EVICT und LOCK.

@Entitypublic class Publisher { private Long id; ... @OneToMany(mappedBy = "publisher", cascade = { CascadeType.PERSIST, CascadeType.MERGE }) private Set<Book> books = new HashSet<Book>(); ... public void addBook(Book book) { this.books.add(book); book.setPublisher(this); } ...}

@Entitypublic class Book {

Listing 4.27: User.java und Book.java mit Kaskadierung

org.hibernate.annotations. CascadeType

Beschreibung

Tabelle 4.5: Hibernate CascadeType Annotations (Forts.)

Page 92: Jpa Mit en

Assoziationen

JPA mit Hibernate 91

private Long id; ... @ManyToOne private Publisher publisher; ...}

@Test public void testPublisherBookCascadeTypes(){ // EntityManager holen und Transaktion beginnen EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); // Publisher in der Datenbank speichern Publisher publisher = new Publisher("MyNewPublisher"); em.persist(publisher); tx.commit(); em.close(); // Nochmals EntityManager holen und Transaktion beginnen em = JpaUtil.getEntityManagerFactory().createEntityManager(); tx = em.getTransaction(); tx.begin(); // Bereits persistenten Publisher aus Datenbank holen Query query = em.createQuery("SELECT p " + "FROM Publisher p WHERE p.description='MyNewPublisher'"); Publisher publisherReloaded = (Publisher)query.getSingleResult(); // Nicht persistentes Book dem Publisher hinzufügen Book book = new Book("Testbuch", "123456789", "Max Muster"); publisherReloaded.addBook(book); // Ein expliziter Aufruf von persist(book) ist durch // CascadeType.PERSIST nicht nötig tx.commit(); em.close(); // Außerhalb des EntityManagers, z.B. beim Client // Publisher und Book sind nun "detached Objects". // Book wird am Client geändert. Book.setTitle("Testbuch NEU"); // EntityManager holen und Transaktion beginnen em = JpaUtil.getEntityManagerFactory().createEntityManager();

Listing 4.28: Beispiel für CascadeType.PERSIST und CascadeType.MERGE

Listing 4.27: User.java und Book.java mit Kaskadierung (Forts.)

Page 93: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

92

4.5 VererbungJPA macht nun auch die Abbildung von Vererbung in relationale Datenbanken möglich. Im Gegensatz zu EJB 2.1 unterstützte Hibernate schon immer Vererbung, daher ist es auch nicht verwunderlich, dass die Strategien zur Abbildung von Vererbungshierarchien von Hibernate in die Spezifikation des JPA mit eingeflossen sind.

JPA bietet drei verschiedene Vererbungsstrategien:

� SINGLE_TABLE: eine Tabelle für alle Klassen der Vererbungshierarchie,

� JOINED :für jede konkrete und abstrakte Klasse eine Tabelle,

� TABLE_PER_CLASS :für jede konkrete Klasse eine Tabelle.

Jede der genannten Strategien hat Vor- und Nachteile. Die nächsten Abschnitte sollen die Auswahl der richtigen Strategie erleichtern. Um Performanceverluste zu vermeiden, sollte die Vererbungsstrategie von Fall zu Fall gewählt werden. In Abbildung 4.4 sieht man eine Vererbungshierarchie aus unserem Beispielprojekt, die Entities AudioBook und Paperback erben von der Entity AbstractBook. Anhand dieser Vererbungshierarchie wer-den die einzelnen Strategien vorgestellt.

Abbildung 4.4: Vererbungshierarchie aus dem Beispielprojekt

tx = em.getTransaction(); tx.begin(); // Publisher wird dem EntityManager wieder hinzugefügt, // diese erkennt die Änderungen am in der Beziehung stehenden // Book und wird diese mit der Datenbank synchronisieren em.merge(publisherReloaded); tx.commit(); em.close(); }

Listing 4.28: Beispiel für CascadeType.PERSIST und CascadeType.MERGE (Forts.)

AbstractBook-title: String-ISBN: String-author: String

AudioBook-medium: String-lengthInMinutes: int

Paperback-pages: int

Page 94: Jpa Mit en

Vererbung

JPA mit Hibernate 93

4.5.1 SINGLE_TABLE

Die einfachste der drei Möglichkeiten ist SINGLE_TABLE. Alle Klassen der Vererbungs-hierarchie werden hier in einer einzigen Tabelle abgebildet. Mit der Annotation @Inheri-tance kann die Vererbungsstrategie bestimmt werden, SINGLE_TABLE ist auch die Default-Strategie, falls keine Strategie angegeben wurde. Für SINGLE_TABLE und JOINED benötigt der O/R-Mapper einen so genannten Discriminatorvalue, um unterscheiden zu können, auf was für ein Objekt ein Eintrag in der Tabelle gemappt werden muss. Für jede Klasse in der Vererbungshierarchie muss ein Discriminatorvalue bestimmt werden. Dazu wird die Annotation @DiscriminatorValue verwendet. Optional kann mit der Annotation @DiscriminatorColumn der Typ und der Name der Discriminator-Spalte bestimmt werden. Die Default-Werte für Typ und Name sind String und DTYPE.

In Listing 4.29 sieht man die Definitionen der Klassen aus der Vererbungshierarchie in Abbildung 4.4. In der abstrakten Klasse AbstractBook wird die Vererbungsstrategie bestimmt, und in den abgeleiteten konkreten Klassen AudioBook und Paperback ist jeweils der Discriminatorvalue definiert.

@Entity@Inheritance(strategy = InheritanceType.SINGLE_TABLE)public abstract class AbstractBook implements Serializable { @Id @GeneratedValue protected Long id; protected String name; protected String ISBN; protected Publisher publisher; ...}@Entity@DiscriminatorValue(value = "AudioBook")public class AudioBook extends AbstractBook { private String medium; private double lengthInMinutes; ...}

@Entity@DiscriminatorValue(value = "Paperback")public class Paperback extends AbstractBook implements Serializable { private int pages; ...}

Listing 4.29: Klassendefinitionen für Vererbungsstrategie SINGLE_TABLE

Page 95: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

94

Hibernate würde für dieses Beispiel die Tabelle aus Abbildung 4.5 erstellen.

Abbildung 4.5: Datenbanktabelle T_BOOK bei Vererbungsstrategie SINGLE_TABLE

Die Einträge der Tabelle in Abbildung 4.5 verdeutlichen schon den ersten Nachteil der Strategie SINGLE_TABLE, nämlich unnötige Spalten. Für eine Entity Paperback werden die Spalten medium und lengthInMinutes nicht benötigt. Umgekehrt ist für eine Entity Audio-Book die Spalte pages unnötig. Das heißt, größere Vererbungshierarchien können schnell zu einer sehr großen Tabelle mit vielen unnötigen Spalten führen. Daraus folgt ein weite-rer Nachteil, der die Datenintegrität der Datenbank betrifft. Alle Felder der abgeleiteten Klassen müssen nullable sein.

Größter Vorteil der Vererbungsstratgie SINGLE_TABLE ist die Performance. Für alle Abfra-gen, sei es eine polymorphe über AbstractBooks (select b from AbstractBook b where...) oder Abfragen über alle AudioBooks (select b from AudioBook b where...), muss immer nur ein Select-Statement zur Datenbank abgesetzt werden. Natürlich werden sich sehr große Tabellen auch auf die Performance auswirken.

Zusammenfassend folgt daraus, dass die Strategie SINGLE_TABLE für Vererbungshie-rarchien mit Subklassen, die nur wenige Attribute haben und polymorphe Abfragen benötigen, sehr gut geeignet ist. Falls aber polymorphe Abfragen nicht benötigt werden, kann die Vererbungsstrategie TABLE_PER_CLASS die bessere Wahl sein.

4.5.2 TABLE_PER_CLASS

Anstatt alle Klassen einer Vererbungshierarchie in eine Tabelle zu stecken, wird bei der Vererbungsstrategie TABLE_PER_CLASS für jede konkrete Klasse eine Tabelle angelegt. Da hier die Tabellen eindeutig den Entities zugeordnet werden können, wird kein Unter-scheidungsfeld (Discriminator) benötigt. Es ist zu beachten, dass die Persistenzprovider nach der Spezifikation der JPA nicht verpflichtet sind, diese TABLE_PER_CLASS-Strategie bereitzustellen. Mit Hibernate muss allerdings auf diese Strategie nicht verzichtet wer-den. Was sich an den Klassendefinitionen im Vergleich zur SINGLE_TABLE-Strategie ändert, sieht man in Listing 4.30.

@Entity@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)public abstract class AbstractBook implements Serializable { ...

Listing 4.30: Klassendefinitionen für Vererbungsstrategie TABLE_PER_CLASS

Page 96: Jpa Mit en

Vererbung

JPA mit Hibernate 95

Die Tabellen AUDIOBOOK und PAPERBACK enthalten neben ihren eigenen Attributen auch alle Attribute der geerbten Klasse. Das hat den Nachteil, dass, wenn ein Attribut der Super-klasse geändert wird, alle Tabellen der Subklassen geändert werden müssen. Ein weiterer Nachteil dieser Strategie ist, dass polymorphe Abfragen nicht optimal unterstützt werden können. Abfragen über alle AbstractBooks enden dann meist in mehreren SQL-Selects, wie zum Beispiel:

Hibernate unterstützt allerdings SQL-Unions, damit reduziert sich die Abfrage wieder auf einen Datenbankzugriff:

Absolut unbrauchbar wird die Strategie TABLE_PER_CLASS, wenn polymorphe Asso-ziationen ins Spiel kommen. Von der Entity Publisher soll mit getAbstratctBooks() auf alle Bücher zugegriffen werden, das heißt, der Fremdschlüssel zu Publisher müsste eigentlich in AbstratctBook sein. AbstratctBook ist aber eine abstrakte Klasse und wird daher nicht als Tabelle in der Datenbank gemappt. In diesem Fall müssten alle Subklas-sen, hier AudioBook und Paperback, einen Fremdschlüssel auf Publisher haben. Es wird empfohlen, hierfür eine der anderen beiden Vererbungsstrategien zu wählen.

Vorteil dieser Strategie ist, dass Abfragen auf konkrete Klassen sehr einfach und perfor-mant sind:

Wie bereits im Kapitel der Vererbungsstrategie SINGLE_TABLE erwähnt, ist TABLE_PER_CLASSfür Vererbungshierarchien ohne polymorphe Abfragen und Beziehungen gut geeignet.

}@Entitypublic class AudioBook extends AbstractBook { private String medium; private double lengthInMinutes; ...}@Entitypublic class Paperback extends AbstractBook implements Serializable { private int pages; ...}

select id, name, medium, lengthInMinutes from AudioBook where ...select id, name, pages from Paperback where ...

select * from ( select id, name, ..., 'A' as type from AudioBook union select id, pages, 'P' as type from Paperback ) where ...

select id, name, ..., pages from Paperback

Listing 4.30: Klassendefinitionen für Vererbungsstrategie TABLE_PER_CLASS (Forts.)

Page 97: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

96

4.5.3 JOINED

Bei der Vererbungsstrategie JOINED wird für jede abstrakte und konkrete Klasse eine Tabelle angelegt. Listing 4.31 zeigt die Implementierung der Vererbungshierarchie mit der Strategie JOINED.

Aus dieser Vererbungshierarchie generiert Hibernate drei Tabellen:

� ABSTRACTBOOK: mit den Spalten id, name und ISBN,

� AUDIOBOOK: mit den Spalten id, medium und lengthInMinutes,

� PAPERBACK : mit den Spalten id und pages.

Jede Tabelle hat einen Primärschlüssel id, der zugleich auch Fremdschlüssel zur Super-klasse ist. Es ist zu beachten, dass Hibernate für die JOINED Strategie keinen Discrimi-natorvalue benötigt. JPA erlaubt es aber, diesen zu definieren, da Implementierungen anderer Persistenzprovider eventuell einen Discriminatorvalue benötigen.

JOINED hat den Vorteil, dass die Datenbankintegrität nicht verletzt wird, da beispiels-weise Attribute in den Subklassen nicht nullable sein müssen. Wesentlich komplexer werden allerdings die Abfragen. Für eine polymorphe Abfrage über alle AbstractBooks muss Hibernate ein Select-Statement mit outer joins generieren, und für eine Abfrage auf eine konkrete Klasse benötigt ein Select-Statement immer inner joins:

@Entity@Inheritance(strategy=InheritanceType.JOINED)public abstract class AbstractBook implements Serializable { ...}@Entitypublic class AudioBook extends AbstractBook { private String medium; private double lengthInMinutes; ...}

@Entitypublic class Paperback extends AbstractBook implements Serializable { private int pages; ...}

Listing 4.31: Klassendefinitionen für Vererbungsstrategie JOINED

select b.id, b.name, p.pages from Paperback p inner join Book b on b.id = p.id where ...

Page 98: Jpa Mit en

Collections

JPA mit Hibernate 97

Die komplexen Abfragen mit join können sich bei größeren Vererbungshierarchien auf die Performance sehr schlecht auswirken.

Die Frage ist nun, welche Strategie man für die Vererbungshierarchie aus dem Bei-spielprojekt wählen soll. TABLE_PER_CLASS kommt wegen der mangelnden Unterstützung für polymorphe Abfragen und Beziehungen nicht in Frage. Wenn die Subklassen Audio-Book und Paperback viele und sich stark unterscheidende Attribute hätten, wäre JOINEDeine gute Wahl. Bei einer einfachen Vererbungshierarchie, wie sie hier vorliegt, ist SINGLE_TABLE die beste Wahl. SINGLE_TABLE erlaubt polymorphe Abfragen und Beziehungen, und da immer nur auf eine Tabelle zugegriffen werden muss, wird die Performance sehr akzeptabel sein.

4.6 CollectionsIn den folgenden Abschnitten sollen die Möglichkeiten und Spezialitäten im Umgang mit Collections näher beleuchtet werden. Ein Teil des Themas wurde bereits in Abschnitt 4.4 betrachtet, da die *-n-Beziehungen als Collection abgebildet werden. An dieser Stelle wird allerdings noch näher auf die Sortierung und die Indizierung von Collections ein-gegangen. Daneben wird betrachtet, wie Collections von primitiven Datentypen oder Value Types gemappt werden können.

4.6.1 Persistente Collections

Die Verwendung von Collections als Instanzvariablen von persistenten Klassen ist in JPA 1.0 leider auf die Verwendung von Assoziationen zwischen Entities beschränkt. Jedoch können in Hibernate und JPA 2.0 auch Collections von so genannten Value Types, also Typen, die keine eigenständigen Entities innerhalb der Datenbank repräsentieren, ver-wendet werden. Ein einfaches Beispiel ist eine Liste von Strings als Attribut einer persis-tenten Klasse.

Um Collections als Attribute von persistenten Klassen in Hibernate zu verwenden, ist es notwendig, deren Typ als Interface zu deklarieren.

Folgende Interfaces kommen hierfür in Frage:

� java.util.Set

� java.util.Collection

� java.util.List

� java.util.Map

� java.util.SortedSet

� java.util.SortedMap

Diese Liste lässt sich durch selbst definierte Interfaces erweitern. Voraussetzung hierfür ist die Implementierung von org.hibernate.usertype.UserCollectionType.

Page 99: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

98

Bei der Verwendung von Collections in persistenten Klassen muss stets darauf geachtet werden, dass sie nur über die obigen Interfaces verwendet werden. So führt der Code in Listing 4.32 zu einer Laufzeit-Exception:

Die letzte Anweisung in Listing 4.32 führt zu einer ClassCastException, da Hibernate das HashSet während persist() durch eine eigene Implementierung ersetzt.

Collection-Instanzvariablen werden wie alle anderen Instanzvariablen automatisch persistent abgespeichert, sobald sie von einer Entity referenziert werden, und gelöscht, wenn in einer Entity keine Referenz mehr auf sie existiert. Dabei gilt es zu beachten, dass es nicht zulässig ist, wenn zwei Entities auf die gleiche Collection-Instanz verweisen. Außerdem ist zu beachten, dass Hibernate nicht zwischen einer Null-Referenz auf eine Collection und einer leeren Collection unterscheidet.

Da die JPA-1.0-Spezifikation kein Mapping von Collections aus Basistypen unterstützt, ist für diesen Fall die Hibernate-spezifische Annotation @org.hibernate.annotations.Coll-ectionOfElements vorhanden.

Durch Verwendung dieser Annotations lassen sich Collections mit List-, Bag-, Set- und Map-Semantik mappen.

User user = new User();Set emailAddresses = new HashSet<String>();emailAddresses.add("[email protected]");user.setEmailAddresses(emailAddresses);em.persist(user);emailAddresses = user.getEmailAddresses(); // okHashSet emailsAsHashSet = (HashSet) user.getEmailAddresses(); // Runtime Exception !

Listing 4.32: Falsche Verwendung von Collections

Neu in JPA 2.0

Die Unterstützung für Collections von Basistypen war die erste Erweiterung die in die zweite Version des JPA aufgenommen wurde. Nun können mittels der Annotation @ElementCollection Collections von Basistypen (beispielsweise String) zu Entities hinzugefügt werden:

@ElementCollection private Set<String> sectionHeaders = new HashSet<String>();

In Kombination mit den Annotationen @Temporal, @Lob und @Enumerated kön-nen auch Datums- und Binärwerte sowie Enumerationen in den Collections abgelegt werden.

i

Page 100: Jpa Mit en

Collections

JPA mit Hibernate 99

Im folgenden Beispiel wird die bereits vorgestellte Klasse AudioBook dahingehend erwei-tert, dass es möglich ist, Kapitelüberschriften anzugeben (Listing 4.33). Die Kapitelüber-schriften werden in einem Set<String> sectionHeaders abgespeichert. Mit @CollectionOf-Elements wird deklariert, dass es sich bei der Instanzvariablen sectionHeaders um eine Collection aus Value-Typen handelt. Mit @JoinTable kann man die Tabelle, in die die Kapitelüberschriften eingetragen werden, angeben. @JoinColumn enthält dabei die Tabel-lenspalte mit dem Fremdschlüssel der Entity. Mit @Column wird der Name der Tabellen-spalte definiert, die die Kapitelüberschriften enthalten soll.

Der Beispielcode in Listing 4.34 erzeugt die Tabellen mit dazugehörigem Inhalt in Abbil-dung 4.6.

Neu in JPA 2.0 (Fortsetzung)

Per Default wird die Collection in einer Datenbanktabelle gespeichert, die sich aus dem Namen der Entity und dem Attributnamen der Collection, getrennt durch einen Unterstrich, zusammensetzt. Mit der Annotation @Collec-tionTable kann die Definition der Datenbanktabelle für die Collection über-schrieben werden.

@Entity@DiscriminatorValue(value="AudioBook")public class AudioBook extends AbstractBook implements Serializable { ... @CollectionOfElements @JoinTable(name = "SECTIONHEADERS", joinColumns = @JoinColumn(name = "AUDIOBOOK_ID")) @Column(name = "sectionheader", nullable = false) private Set<String> sectionHeaders = new HashSet<String>(); ...}

Listing 4.33: Klasse „AudioBook“ mit Kapitelüberschriften

@Testpublic void testAudioBookWithSectionHeaders() { ... AudioBook audioBook = new AudioBook("Kafkas Gesammelte Werke", "x-1234556-x", "Franz Kafka", 56.45, "CD"); Set<String> sectionHeaders = new HashSet<String>(); sectionHeaders.add("Die Verwandlung"); sectionHeaders.add("In der Strafkolonie"); sectionHeaders.add("Das Urteil");

Listing 4.34: Test für Klasse „AudioBook“ mit Kapitelüberschriften

i

Page 101: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

100

Abbildung 4.6: Datenbanktabelle „Abstractbook“ und „abstractbook_sectionheaders“

4.6.2 Collections mit Index oder Schlüssel

Möchte man eine Collection oder Assoziation indizieren (Verwendung von List) oder auf die Elemente mit einem Schlüssel zugreifen (Verwendung von Map) müssen einige Besonderheiten beachtet werden. So ist das Speichern der Reihenfolge der Elemente in der Liste nicht mit JPA 1.0, sondern nur mit JPA 2.0 oder einer Hibernate-spezifischen Annotation möglich. Die Definition des Attributs für den Schlüssel einer Map kann man jedoch auch mit JPA ab Version 1.0 durchführen.

Assoziation als Map

Um die Verwendung einer Map für eine 1-zu-n-Beziehung zu zeigen, wird die Assozia-tion zwischen Publisher und Book erweitert.

In Listing 4.35 sieht man die veränderten Klassen. Mit der Annotation @MapKey wird der Primärschlüssel id von MapBook als Schlüssel für die Map definiert. Dies ist auch der Default, sodass die Angabe von (name="id") hier auch weggelassen werden könnte.

sectionHeaders.add("Ein Hungerkünstler"); audioBook.setSectionHeaders(sectionHeaders); em.persist(audioBook); ...}

Listing 4.34: Test für Klasse „AudioBook“ mit Kapitelüberschriften (Forts.)

Page 102: Jpa Mit en

Collections

JPA mit Hibernate 101

In Listing 4.36 werden der Aufbau und das Speichern der Map getestet. Beim Weglassen der Annotation @MapKey würde das Speichern und Laden der Map anstandslos funktio-nieren, jedoch würde der Zugriff auf die Elemente einen Fehler erzeugen, da die Schlüs-sel mit null initialisiert wären.

Das Mapping auf Datenbankebene ist bei der Verwendung einer Map zum Abbilden der 1-zu-n-Beziehung unverändert.

@Entitypublic class Publisher implements Serializable { ... @OneToMany(mappedBy = "publisher", cascade=CascadeType.ALL) @MapKey(name="id") private Map<Long,MapBook> bookMap = new HashMap<Long, MapBook>(); ...}@Entitypublic class MapBook implements Serializable { // andere Attribute analog zu Klasse Book ... @ManyToOne private Publisher publisher; ... }

Listing 4.35: Klasse „Publisher“ mit Map von MapBooks

@Testpublic void testPublisherBookMap() { EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Publisher publisher = new Publisher("My Really New Publisher"); em.persist(publisher); MapBook mp = new MapBook("Die Verwandlung", "x-1234567-x", "Franz Kafka"); MapBook mp2 = new MapBook("Der Hungerkünstler", "x-2345678-x", "Franz Kafka"); mp.setPublisher(publisher); mp2.setPublisher(publisher); em.persist(mp2); em.persist(mp); publisher.getBookMap().put(mp.getId(), mp); publisher.getBookMap().put(mp2.getId(), mp2); tx.commit();

Listing 4.36: Test der Klasse „Publisher“ mit Map von MapBooks

Page 103: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

102

Es können auch andere Attribute der referenzierten Klasse als Schlüssel für die Map ver-wendet werden. Voraussetzung ist allerdings, dass für das entsprechende Attribut ein Unique Constraint definiert werden muss. Listing 4.37 zeigt die geänderten Klassen und Listing 4.38 den angepassten Testfall.

em.close();

em = JpaUtil.getEntityManagerFactory().createEntityManager(); Publisher publisherReloaded = em.find(Publisher.class, publisher.getId()); // Fehler beim Lesen der Map, wenn @MapKey entfernt würde System.out.println(publisherReloaded.getBookMap()); em.close();}

Neu in JPA 2.0

In JPA 2.0 kann man nun Basistypen als Schlüssel und als Elemente in einer Map verwenden. Hierbei kommt ebenfalls die Annotation @ElementCollectionzum Einsatz:

@ElementCollection @MapKey Map<Long, String> bookNames;

Für die Elemente der Map können mit @Temporal, @Lob und @Enumerated auch Datums- und Binärwerte sowie Enumerationen verwendet werden. Möchte man Datumswerte oder Enumerationen als Schlüssel verwenden, so muss man dies über die Annotationen @MapKeyTemporal und @MapKeyEnumerated definieren.

@Entitypublic class Publisher implements Serializable { ... @OneToMany(mappedBy = "publisher", cascade=CascadeType.ALL) @MapKey(name="ISBN") private Map<String,MapBook> bookMap = new HashMap<String, MapBook>(); ...}@Entity@Table(uniqueConstraints=@UniqueConstraint(columnNames="ISBN"))public class MapBook implements Serializable { ...}

Listing 4.37: Angepasste Klasse „Publisher" mit Map von MapBooks

Listing 4.36: Test der Klasse „Publisher“ mit Map von MapBooks (Forts.)

i

Page 104: Jpa Mit en

Collections

JPA mit Hibernate 103

Index für List mit Hibernate

Mit JPA 1.0 kann der Index einer Liste nicht direkt mit in der Datenbank persistiert wer-den. Die Reihenfolge der Elemente der Liste ist demzufolge beim Laden nicht sicherge-stellt. In Hibernate kann mit der Annotation @IndexColumn eine zusätzliche Spalte defi-niert werden, die den Index der Liste enthält.

In Listing 4.39 wird für das Attribut sectionHeaders der Klasse AudioBook eine Liste mit @IndexColumn definiert.

@Testpublic void testPublisherBookMap() { ... publisher.getBookMap().put(mp.getISBN(), mp); publisher.getBookMap().put(mp2.getISBN(), mp2); ...}

Listing 4.38: Angepasster Test der Klasse „Publisher" mit Map von MapBooks

Neu in JPA 2.0

Mit der Annotation @OrderColumn kann mit JPA 2.0 die Reihenfolge der Ele-mente einer Collection persistiert werden. Dabei wird ein separate Spalte in der Datenbank angelegt, die per Default nach dem Namen des referenzieren-den Properties und '_ORDER' benannt wird. Die Spalte ist nicht für die Entity sichtbar und kann demzufolge nicht selbst gelesen oder geschrieben werden. Der PersistenceProvider ist dafür verantwortlich, dass Umsortierungen sowie das Löschen und Einfügen von Elementen korrekt in der Datenbank persis-tiert werden. Verwenden kann man @OrderColumn für 1-zu-n- und n-zu-m-Beziehungen sowie für Collections von Basistypen.

@Entity@DiscriminatorValue(value="AudioBook")public class AudioBook extends AbstractBook implements Serializable { ... @CollectionOfElements @IndexColumn(name="index" base=1) private List<String> sectionHeaders = new ArrayList<String>(); ...}

Listing 4.39: Klasse „AudioBook" mit Liste von Kapitelüberschriften

i

Page 105: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

104

Neben dem Parameter name kann in @IndexColumn mit base auch noch der „Startwert“ für den Index angegeben werden. Der Default für base ist 0, jedoch kann man mit der Angabe von bspw. base=3 den Index mit „Drei“ beginnen lassen, sofern man dies als sinnvoll erachtet.

Zum Testen kann man den Code aus Listing 4.34 im Kapitel 4.6.1 verwenden. Es muss lediglich das Set durch List ausgetauscht werden.

4.6.3 Sortierte Collections

Der Inhalt von Collections kann beim Laden der Daten bereits von der Datenbank sor-tiert werden. Für diesen Zweck ist die Annotation @OrderBy in der JPA-Spezifikation ent-halten. Durch die Angabe einer kommaseparierten Liste von Entity-Instanzvariablen sowie der Sortierungsart (asc – aufsteigend oder desc – absteigend) kann die Sortierung der Collection festgelegt werden.

In Listing 4.40 wird das Set von Books des Publishers aufsteigend nach Autor sortiert.

Die Angabe der Annotation @OrderBy führt zur Erweiterung der SQL-Query um eine order-by-Klausel entsprechend den angegebenen Attributen. Im Folgenden ist ein Teil der erweiterten Query zu sehen.

Wird der optionale Parameter der Annotation @OrderBy nicht angegeben, so wird eine aufsteigende Sortierung nach Primärschlüssel vorgenommen.

Da die JPA-Spezifikation in Version 1.0 keine Collections von Wertetypen unterstützt, gibt es in Hibernate eine entsprechende Annotation für die Sortierung dieser Collections: Mit @org.hibernate.annotations.OrderBy wird eine SQL-Anweisung definiert, die beim Laden der Daten aus der Datenbank verwendet wird.

@Entity ... @OrderBy(value="author asc") private List<Book> books; ...}

Listing 4.40: Klasse „Publisher“ mit „Books“, die beim Laden nach „Autor“ sortiert werden

... where books0_.publisher_myId=? order by books0_.author asc

Page 106: Jpa Mit en

Collections

JPA mit Hibernate 105

In Listing 4.41 wird die Klasse AudioBook mit einem normalen List<String> für die Kapitel-überschriften dargestellt. Der Inhalt der List wird beim Laden der Daten aus der Daten-bank sortiert. Dies wird durch die Annotation @org.hibernate.annotations.OrderByerreicht, die als Parameter die SQL-Sortieranweisung enthält.

In Hibernate steht darüber hinaus noch eine weitere Möglichkeit für die Verwendung von sortierten Collections zur Verfügung.

Hibernate unterstützt Collections, die java.util.SortedMap und java.util.SortedSet imp-lementieren. Um diese Collections zu mappen, muss die Annotation @org.hiber-nate.annotations.Sort angegeben werden, die als Parameter die Art der Sortierung (NATU-RAL, UNSORTED, COMPARATOR) und eventuell eine Comparator-Klasse angibt:

In Listing 4.42 wird die Klasse Paperback gezeigt, in der die Überschriften als SortedSetverwaltet werden. Im Unterschied zu @OrderBy wird bei @Sort die SQL-Query nicht ange-passt, da die Sortierung in der Collection stattfindet.

Neu in JPA 2.0

Die Annotation @OrderBy wurde in JPA 2.0 erweitert, so können nun Collec-tions von Basistypen ebenfalls sortiert werden. Daneben kann mit der Punkt-notation nach Attributen innerhalb einer Komponente sortiert werden:

public class Application { // Definition von User und Address in Listing 4.7 und Listing 4.8 @ElementCollection @OrderBy("address.city") private Set<User> users; ...

Wird für eine Collection @OrderColumn verwendet, so wird die Annotation @OrderBy ignoriert.

@Entity@DiscriminatorValue(value="AudioBook")public class AudioBook extends AbstractBook implements Serializable { ... @CollectionOfElements @Column(name = "sectionheader", nullable = false) @org.hibernate.annotations.OrderBy(clause="sectionheader") private List<String> sectionHeaders = new ArrayList<String>(); ...}

Listing 4.41: Sortieren der Kapitelüberschriften mit @org.hibernate.annotations.OrderBy

@Sort(type = SortType.Comparator, comparator = BookComparator.class)

i

Page 107: Jpa Mit en

4 – Der Aufbau und das Mapping von Entities

106

4.7 EnumerationsMit Enumerations kann man sehr gut eine definierte Menge von Werten darstellen und diese als Konstanten verwenden. Das Attribut medium der Klasse AudioBook soll als Enu-meration angegeben und persistent in der Datenbank gespeichert werden. In Listing 4.43 werden die Änderungen an der Klasse gezeigt.

Per Default muss die Annotation @Enumerated nicht angegeben werden, um ein Attribut vom Typ Enumeration zu mappen. In diesem Fall werden die Werte des Attributs als Ordinalzahl in der Datenbank gespeichert. Die Ordinalzahl gibt die Position des defi-nierten Wertes innerhalb der Enumeration an. Will man stattdessen den Namen der Enu-meration speichern, so muss die Annotation @Enumerated(EnumType.STRING) angegeben werden.

@Entity@DiscriminatorValue(value="Paperback")public class Paperback extends AbstractBook implements Serializable { ... @CollectionOfElements @Sort(type=SortType.NATURAL) private SortedSet<String> captions = new TreeSet<String>(); ...}

Listing 4.42: Verwendung von SortedSet

@Entity@DiscriminatorValue(value="AudioBook")public class AudioBook extends AbstractBook implements Serializable { ... @Enumerated(EnumType.ORDINAL) private Media medium; ... public enum Media { CD, CASSETTE, DVD, MP3; }}

Listing 4.43: Anpassungen an AudioBook für die Verwendung von Enumerations

Page 108: Jpa Mit en

Zusammenfassung

JPA mit Hibernate 107

4.8 ZusammenfassungIn diesem Kapitel wurden die vielfältigen Möglichkeiten der Beziehungen zwischen den Entites vorgestellt. Hibernate hatte gegenüber JPA 1.0 vor allem den Vorteil, dass auch Collections von Basistypen gemappt werden konnten. Jedoch hat JPA 2.0 diese Lücke in der Spezifikation geschlossen. Zwar bietet Hibernate immer noch an der einen oder anderen Stelle mehr Möglichkeiten beim Mapping, allerdings fällt die Entscheidung nun deutlich schwerer, ob es sich lohnt, die Portabilität einer Anwendung für die zusätzli-chen Möglichkeiten von Hibernate aufzugeben.

An dieser Stelle ist man nun in der Lage, objektorientierte Domainmodelle mittels Anno-tationen der Java Persistence API und dem Hibernate-Mapping abzubilden. Feingranu-lare Objektmodelle können mittels Komponenten in grobgranulare Tabellen gemappt werden, und Referenzen sind in der Datenbank als Fremdschlüssel wiederzufinden. Ver-erbungshierarchien können ebenfalls gemappt werden, dazu wurden drei verschiedene Strategien vorgestellt und Vor- und Nachteile erläutert. In diesem Kapitel wurden häufig Collections verwendet, gerade im Zusammenhang mit Assoziationen wird die Verwen-dung als selbstverständlich angenommen.

Page 109: Jpa Mit en
Page 110: Jpa Mit en

JPA mit Hibernate 109

5 Lebenszyklus einer Entity

In diesem Kapitel wird der Lebenszyklus der persistenten Objekte näher vorgestellt. Da diese Zustände transparent verwaltet werden, hat man normalerweise nicht direkt damit zu tun. Um eine stabile Lösung zu entwickeln und eine optimale Performance zu errei-chen, ist es aber sinnvoll, wenn man die Zustände der Entities im Hinterkopf behält.

5.1 Die Zustände einer EntityEine Entity kann sich grundsätzlich in drei verschiedenen Zuständen befinden: Tran-sient, Persistent oder Detached. Dabei kann man bei transienten Objekten zwischen neu angelegten und gelöschten Objekten unterscheiden. Da JPA und Hibernate transparente O/R-Mapping-Frameworks sind, ist den Entities ihr eigener Zustand nicht bekannt. Es ist also nicht direkt möglich, eine Entity nach ihrem aktuellen Zustand zu fragen.

In Abbildung 5.1 sind die drei Zustände und mögliche Übergänge mittels Methoden des EntityManagers dargestellt.

Abbildung 5.1: Die Zustände einer Entity

5.1.1 Transient

Jedes Entity-Objekt, das über den Operator new erzeugt wird, befindet sich zu Beginn im Zustand Transient. In diesem Zustand besteht keine Verbindung zwischen der Entity und einem EntityManager. Der Zustand der Entity wird nicht persistent abgespeichert.

In diesem Zustand verhält sich die Entity wie jedes normale Java-Objekt. Wenn keine Referenz mehr auf das Objekt existiert, wird es vom Garbage Collector entsorgt. Es gibt noch keine Repräsentation der Entity innerhalb der Datenbank, das heißt, alle Daten der

Detached

Persistent

Transientnew

persist()

merge()

remove()

find()

detach()

Neu in JPA 2.0

clear()

Page 111: Jpa Mit en

5 – Lebenszyklus einer Entity

110

Entity sind flüchtig und gehen verloren, sobald der Garbage Collector das Objekt ver-nichtet. Außerdem sind Entities im Zustand Transient nicht Teil einer Transaktion, ihre Änderungen können also nicht durch ein Rollback rückgängig gemacht werden. An die-sem Verhalten ändert sich auch nichts, wenn die Entity Referenzen auf andere Entities enthält, die ebenfalls im Zustand Transient sind. Entsprechende Primärschlüsselfelder, die erst beim Abspeichern in der Datenbank erzeugt werden, sind noch nicht gesetzt.

Der Übergang einer Entity vom Zustand Transient zum Zustand Persistent wird durch den Aufruf der Methode persist() auf dem EntityManager ausgelöst. Alternativ reicht es auch, wenn eine Entity durch eine andere Entity referenziert wird, die bereits im Zustand Persistent ist. Dies ist aber abhängig von den Kaskadierungseinstellungen der Assozia-tion (siehe auch Kapitel 4.4.4).

5.1.2 Persistent

Sobald eine Entity einem EntityManager zugeordnet ist, befindet sie sich im Zustand Persistent. Eine persistente Entity hat noch nicht notwendigerweise eine Repräsentation innerhalb der Datenbank, da der EntityManager nicht nach jedem persist() sofort die entsprechenden Datenbankaufrufe durchführt. Der Entity wird aber auf jeden Fall sofort ein Primärschlüssel zugewiesen und sie ist fortan dem Persistenzkontext des EntityMa-nagers zugeordnet.

Alle Entities im Zustand Persistent sind Teil einer Transaktion, Änderungen können also durch ein Rollback rückgängig gemacht werden. Jede Änderung von Attributen einer persistenten Entity wird automatisch erkannt und führt zu einem entsprechenden UPDATEbzw. INSERT innerhalb der Datenbank. Als Entwickler muss man somit keine manuellen Updates oder Inserts für jede einzelne Entity ausführen.

Eine weitere Möglichkeit, Entities mit dem Zustand Persistent zu erhalten, ist die Aus-führung von Abfragen mittels find(), em.createQuery().getResultList() etc. In diesem Fall gibt es jedoch keinen Übergang von Transient nach Persistent, denn die Entities sind ja von Anfang an im Zustand Persistent, wenn sie aus der Datenbank geladen werden.

Häufig werden Entities nicht einzeln aus der Datenbank geladen, sondern es wird ein kompletter Objektgraph von abhängigen Entities gemappt. In diesem Fall befinden sich natürlich auch alle referenzierten Objekte von Anfang an im Zustand Persistent.

Der Übergang von Persistent zu Transient wird durch den Aufruf von remove() ausge-löst. Durch den Aufruf von remove() werden die Daten der Entity innerhalb der Daten-bank gelöscht. Das Java-Objekt ist nach dem Löschen natürlich immer noch verfügbar, vorausgesetzt, es wird noch referenziert.

5.1.3 Detached

Wird ein EntityManager mittels close() geschlossen, endet die Zuordnung der Entities zu diesem EntityManager. Ab diesem Zeitpunkt sind die Entities nicht mehr Teil einer Transaktion, und Änderungen werden nicht mehr mit der Datenbank synchronisiert. Die Java-Objekte enthalten aber trotzdem persistente Daten, die aber unter Umständen ver-

Page 112: Jpa Mit en

Zustandsänderungen einer Entity

JPA mit Hibernate 111

altet sind, wenn nach dem Schließen des EntityManagers eine Änderung der entspre-chenden Datenbankzeilen durchgeführt wird.

Die Zuordnung einer Entity zu einem EntityManager wird nicht nur durch das Schließen des EntityManagers aufgehoben, sondern auch durch das Serialisieren und Übertragen einer Entity in einen anderen Prozess (Remote-Anwendung etc.). Diese Detached Enti-ties können beispielsweise direkt als Transfer-Objekte zwischen Präsentationsschicht und Datenbankschicht einer mehrschichtigen Anwendung verwendet werden.

JPA bietet einen Mechanismus, mit dem Detached Entities wieder einem EntityManager zugeordnet werden können. Durch den Aufruf der EntityManager-Methode merge()wird eine Entity mit dem Zustand Detached wieder an den EntityManager gebunden und befindet sich dann wieder im Zustand Persistent.

5.2 Zustandsänderungen einer EntityIm folgenden Abschnitt sollen die Methoden zum Ändern des Zustands einer Entity näher vorgestellt werden. Dabei werden nicht nur die Möglichkeiten von EntityManager (JPA) und Hibernate Session vorgestellt, sondern es wird auch auf Besonderheiten, Unterschiede und Gemeinsamkeiten hingewiesen.

5.2.1 Allgemeines zum Synchronisieren von Entities

Eine Entity wird mit dem Commit der Transaktion mit der Datenbank synchronisiert. Dabei werden sowohl neue Zuweisungen von persistenten Attributen als auch die Ver-änderung von bestehenden Werten in die Datenbank übernommen. Bei dem Prozess der Synchronisation werden allerdings nicht veränderte Entities des Persistenzkontexts nicht automatisch aktualisiert. Dafür muss explizit die Operation refresh() ausgeführt werden.

Bidirektionale Beziehungen zwischen im EntityManager verwalteten Entities werden über den Besitzer der Assoziation in der Datenbank gespeichert. Es ist allerdings in der Verantwortung des Entwicklers, die Beziehungen untereinander konsistent zu halten, da sonst Änderungen beim Speichern verloren gehen können.

5.2.2 Methoden des EntityManagers

Es sollen im Folgenden die Methoden des EntityManagers vorgestellt werden. Dabei wird besonders auf die Wirkung der Methoden eingegangen, wenn Entities mit unter-schiedlichen Zuständen übergeben werden.

Neu in JPA 2.0

In JPA 2.0 wurde die refresh()-Methode überladen. Nun können neben der Entity auch Informationen zum Locking (Kapitel 6.2) und Hinweise an den Persistence Provider (Kapitel 6.3) übergeben werden.

i

Page 113: Jpa Mit en

5 – Lebenszyklus einer Entity

112

Suchen mit find() und getReference()

Mit der Methode find() wird eine Entity aus der Datenbank anhand des Typs und des Primärschlüssels geladen. Ist die Entity bereits im Persistenzkontext vorhanden, wird sie aus diesem bereitgestellt. Konnte anhand des Typs und des Primärschlüssels keine Entity gefunden werden, so wird null zurückgegeben.

Die Methode getReference() hat die gleiche Signatur wie find(), jedoch wird eine Entity zurückgegeben, deren Zustand, sofern möglich, per Lazy Fetching (Kapitel 8.1) geladen wird.

Speichern mit persist()

Durch die Methode persist() wird die übergebene Entity in den Persistenzkontext des EntityManagers aufgenommen. Handelt es sich um eine neu erzeugte Entity, so wird diese persistiert. Die entsprechenden Datenbankänderungen werden beim Beenden der Transaktion oder beim Aufruf von flush() vorgenommen.

Ist die übergebene Entity bereits persistiert und wird aktuell vom EntityManager ver-waltet, so wird der Aufruf von persist() ignoriert. Allerdings werden alle Referenzen, sofern die Kaskadierungsoptionen cascade=PERSIST oder cascade=ALL gesetzt sind, eben-falls in den Persistenzkontext eingefügt.

Löschen mit remove()

Mit der Methode remove() wird die übergebene Entity, sofern sie sich im Persistenzkon-text des EntityMangers befindet, zum Löschen markiert. Mit dem Beenden der Trans-aktion oder einem Aufruf von flush() werden die Änderungen in die Datenbank über-nommen. Für alle referenzierten Entities wird die Operation weitergereicht, wenn die Kaskadierungsoptionen cascade=REMOVE oder cascade=ALL gesetzt sind.

Handelt es sich bei der übergebenen Entity um eine neue oder bereits gelöschte Entity, so wird die remove()-Operation ignoriert. Je nach Kaskadierungsoption werden aber die referenzierten Entities gelöscht.

Beim Übergeben einer Detached-Entity an die Operation remove() wird eine IllegalArgu-mentException geworfen.

Synchronisation erzwingen mit flush()

Mit der Methode flush() kann eine Synchronisation der im EntityManager enthaltenen Entities mit der Datenbank erzwungen werden. Referenzen zu neuen Entities, die mit der Kaskadierungsoption cascade=PERSIST oder cascade=ALL gekennzeichnet sind, werden beim Aufruf von flush() automatisch in der Datenbank persistiert. Ist allerdings keine

Neu in JPA 2.0

In JPA 2.0 wurde die find()-Methode überladen. Nun können neben dem Pri-märschlüssel und dem Typ auch Informationen zum Locking (Kapitel 6.2) und Hinweise an den Persistence Provider (Kapitel 6.3) übergeben werden.

i

Page 114: Jpa Mit en

Zustandsänderungen einer Entity

JPA mit Hibernate 113

Kaskadierungsoption gesetzt und eine verwaltete Entity verweist auf eine neue Entity, wird eine IllegalStateException geworfen.

Ist beim Aufruf von flush() eine im EntityManager verwaltete Entity mit einer detached-Entity verbunden, wird die Beziehung nur dann synchronisiert, wenn der Besitzer der Beziehung im Persistenzkontext enthalten ist. Im anderen Fall, die Detached-Entity ist Besitzer der Beziehung, ist das Verhalten undefiniert. Es sollte also vermieden werden, solche Konstellationen zu erzeugen.

Zustand überprüfen mit contains()

Bei der Verwendung von mehreren EntityManagern muss sichergestellt werden, dass ein konkretes Java-Objekt jeweils nur in einem Persistenzkontext (EntityManager) vorhan-den ist, da es sonst zu undefinierten Verhalten kommen kann.

Mit der Methode contains() kann überprüft werden, ob sich eine Entity im EntityMana-ger und somit im Persistenzkontext befindet. Es wird true zurückgegeben, wenn die Entity aus der Datenbank mittels find() oder Queries geladen wurde. Für neue Entities liefert contains() erst nach dem Persistieren mit persist() true zurück. Bei der Übergabe einer gelöschten, neuen oder detached Entity an contains() wird false als Ergebnis bereitgestellt.

Aktualisieren mit merge()

Durch den Aufruf von merge() wird der Zustand der übergebenen Entity in den Entity-Manager übertragen. Dabei wird die Entity selbst nicht in den EntityManager aufgenom-men, sondern stets eine Kopie. Falls sich also bereits eine persistente Entity mit der glei-chen ID im EntityManager befindet, werden die Daten der übergebenen Entity in die persistente Entity kopiert. Falls sich keine passende Entity in der Session befindet, wird versucht, eine aus der Datenbank zu laden oder neu anzulegen, bevor die Daten kopiert werden. Es wird eine Referenz auf die persistente Entity zurückgegeben.

Es ist wichtig zu beachten, dass die als Parameter übergebene Entity nach dem Aufruf von merge() nicht im Persistenzkontext ist, sondern der Rückgabewert der Methode die nun im EntityManager verwaltete Entity enthält. Listing 5.1 soll das Problem verdeut-lichen.

@Testpublic void testUserMerge() { EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager(); em.getTransaction().begin(); User user = new User("Hans", "Hase", "[email protected]"); em.persist(user); em.getTransaction().commit(); em.close();

user.setFirstname("Max");

Listing 5.1: Test der merge()-Methode des EntityManagers

Page 115: Jpa Mit en

5 – Lebenszyklus einer Entity

114

Bei der Übergabe einer neuen Entity an die Operation merge() wird ebenso eine Kopie im EntityManager angelegt, die als Rückgabewert dem Programmierer für die weitere Verwen-dung zur Verfügung gestellt wird. Handelt es sich allerdings bei dem übergebenen Parame-ter um eine bereits gelöschte Entity, so wird eine IllegalArgumentException geworfen.

Ist die Kaskadierungsoption cascade=MERGE oder cascade=ALL gesetzt, werden die entspre-chenden referenzierten Entities ebenfalls in den Persistenzkontext übernommen.

5.2.3 Besonderheiten der Hibernate Session

Betrachtet man die Methoden, welche die Session von Hibernate zur Verfügung stellt, so findet man sehr viele Übereinstimmungen zum EntityManager. Im Folgenden werden die Besonderheiten kurz dargestellt.

� Das Löschen von Entities erfolgt über die Methode delete() statt remove().

� Neben der Methode persist() zum Speichern von Entities kann ebenso die Methode save() verwendet werden.

� Mit der Methode update() wird die übergebene Entity an die Session gebunden. Änderungen an den Daten der Entity werden somit wieder von Hibernate erkannt und automatisch in der Datenbank abgespeichert. Dabei spielt es keine Rolle, ob die Entity vor oder nach dem Aufruf von update() verändert wurde. Die übergebene Entity muss sich allerdings im Zustand Detached befinden. Falls bereits eine Entity mit dem gleichen Primärschlüssel der Session zugeordnet ist, wenn update() aufgerufen wird, wird eine Exception geworfen. Update() sollte somit nur verwendet werden, wenn man sich sicher ist, dass sich eine Entity nicht bereits in einer Session befindet.

� Bei der Methode saveOrUpdate() handelt es sich um eine Convenience-Methode. Abhängig vom Zustand der übergebenen Entity wird diese entweder gespeichert oder aktualisiert. Der Effekt ist also bei Detached Entities identisch mit dem Aufruf von update(). Für Entities im Zustand Transient entspricht das Ergebnis dem Aufruf von save(). Falls sich die Entity bereits im Zustand Persistent befindet, wird nichts unternommen. Für den Fall, dass eine Detached Entity übergeben wird, für deren Pri-märschlüssel bereits eine Entity in der Session existiert, wird aber auch hier eine Exception geworfen.

em = JpaUtil.getEntityManagerFactory().createEntityManager(); em.getTransaction().begin(); User userMerged = em.merge(user); System.out.println(em.contains(user)); // liefert false System.out.println(em.contains(userMerged)); // liefert true em.getTransaction().commit(); em.close();}

Listing 5.1: Test der merge()-Methode des EntityManagers (Forts.)

Page 116: Jpa Mit en

Verwendung von Detached Entities

JPA mit Hibernate 115

5.3 Verwendung von Detached EntitiesNachdem eine Entity in den Zustand Detached übergegangen ist, beispielsweise durch das Schließen des EntityManagers, kann weiterhin mit ihr gearbeitet werden, wobei allerdings einige Besonderheiten zu beachten sind.

Ein häufiges Problem, das in Zusammenhang mit Detached Entities auftritt, hängt mit dem Lazy Loading (siehe auch Kapitel 8) von Assoziationen zusammen. Lazy Loading wird eingesetzt, um Assoziationen zwischen Entities erst später aufzulösen, d. h., die Daten der referenzierten Entities werden erst bei Bedarf geladen. Wird dieses Feature zusammen mit Detached Entities eingesetzt, kommt es leicht zu Fehlern.

In Listing 5.2 wurde die Klasse Paperback um eine Assoziation zur Klasse PaperbackCovererweitert.

Mit fetch = FetchType.LAZY wird das Lazy Loading für diese Assoziation aktiviert. Die Klasse PaperbackCover ist genau wie Paperback eine normale Entity.

Neu in JPA 2.0

Neben dem Schließen des EntityManagers und dem Aufruf von clear(), bei denen alle Entities in den Zustand Detached übergehen, kann man in JPA 2.0 mit der Methode detach() des EntityManagers auch einzelne Entities in den Zustand Detached überführen.

@Entity@DiscriminatorValue(value="Paperback")public class Paperback extends AbstractBook implements Serializable { ... @OneToOne(cascade=CascadeType.ALL, fetch=FetchType.LAZY) private PaperpackCover cover; ...}

@Entitypublic class PaperpackCover implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String pictureName; //vielmehr Daten, BLOB etc. ...}

Listing 5.2: Erweiterung der Klasse „Paperback“ um eine Referenz auf PaperbackCover

i

Page 117: Jpa Mit en

5 – Lebenszyklus einer Entity

116

Bei der Ausführung des Codes in Listing 5.3 kommt es jedoch zu einer Exception.

Beim Aufruf von pbReloaded.getCover().getPictureName() wird eine Exception vom Typ LazyInitializationException geworfen, die signalisiert, dass die Daten der Entity covernicht zur Verfügung stehen, da der EntityManager bereits geschlossen wurde und sich die Entity pbReloaded im Zustand Detached befindet. Die Daten von cover können somit nicht nachgeladen werden.

Um dieses Problem zu vermeiden, muss sichergestellt werden, dass alle Daten einer Detached Entity geladen werden, bevor die Entity in den Zustand Detached übergeht. Dies kann entweder durch den Verzicht auf Lazy Loading, durch entsprechend formu-lierte Queries (Kapitel 7.2.6 und Kapitel 8), durch den vorherigen Zugriff auf das Feld oder durch die Verwendung von Hibernate.initialize(Object proxy) erreicht werden.

Die Exception im vorherigen Beispiel lässt sich z. B. durch die in Listing 5.4 dargestellte Änderung vermeiden.

@Test

public void testLazyLoading() { EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager(); em.getTransaction().begin(); Paperback pb = new Paperback("Test", "1234", "TestAuthor", 2); pb.setCover(new PaperpackCover("TestCover.png")); em.persist(pb); em.getTransaction().commit(); em.close();

//Entity erneut laden em = JpaUtil.getEntityManagerFactory().createEntityManager(); Paperback pbReloaded = em.find(Paperback.class, pb.getId()); em.close(); PaperpackCover cover = pbReloaded.getCover(); System.out.println(cover.getPictureName()); //Exception}

Listing 5.3: Test von Lazy Loading der Klasse „Paperback“

...em = JpaUtil.getEntityManagerFactory().createEntityManager();Paperback pbReloaded = em.find(Paperback.class, pb.getId());pbReloaded.getCover().getPictureName();em.close();

Listing 5.4: Zugriff auf Attribute vor den Übergang in den Zustand „Detached“

Page 118: Jpa Mit en

Verwendung von Detached Entities

JPA mit Hibernate 117

Da es allerdings eher unpraktisch ist, auf jedes Feld einer Entity vor dem „Detachen“ zuzugreifen, gibt es in Hibernate die Methode Hibernate.initialize(), die in Listing 5.5angewandt wird.

Da die Methode Hibernate.initialize() allerdings nicht sicherstellt, dass evtl. referen-zierte Entities innerhalb der übergebenen Entity initialisiert werden, ist sie auch nicht für größere Objektgraphen geeignet. Zum einen müsste der gesamte Graph durchlaufen werden, um alle Elemente zu initialisieren, und zum anderen würden in so einem Fall viele einzelne Abfragen an die Datenbank gestellt werden, ehe der gesamte Graph initia-lisiert wäre.

PaperpackCover cover = pbReloaded.getCover();System.out.println(cover.getPictureName()); // keine Exception...

...em = JpaUtil.getEntityManagerFactory().createEntityManager();Paperback pbReloaded = em.find(Paperback.class, pb.getId());Hibernate.initialize(pbReloaded.getCover());em.close();

PaperpackCover cover = pbReloaded.getCover();System.out.println(cover.getPictureName()); // keine Exception...

Listing 5.5: Auflösen aller Referenzen mit Hibernate.initalize()

Neu in JPA 2.0

In JPA 2.0 wurden zwei neue Interfaces, PersistenceUtil und PersistenceUnit-Util, eingeführt, die überprüfen können, ob eine Entity geladen ist oder nicht. Dazu stehen die Methoden isLoaded(Object entity) zur Überprüfung einer Entity und isLoaded(Object entity, String attributeName) zur Überprüfung eines Attributs einer Entity zur Verfügung.

Dabei gilt eine Entity als geladen, wenn alle mit FetchType.EAGER (Kapitel 8) markierten Attribute aus der Datenbank geholt oder von der Anwendung gesetzt wurden.

Der Unterschied zwischen beiden Interfaces ist, dass PersistenceUtil alle bekannten Persistence Units überprüft und PersistenceUnitUtil dies nur für eine bestimmte Persistence Unit vornimmt. Mit javax.persistence.Persis-tence.getPersistenceUtil() und EntityManagerFactory emf; emf.getPersis-tenceUnitUtil() werden Instanzen der beiden Interfaces bereit gestellt.

Listing 5.4: Zugriff auf Attribute vor den Übergang in den Zustand „Detached“ (Forts.)

i

Page 119: Jpa Mit en

5 – Lebenszyklus einer Entity

118

Der sinnvollste Weg, das Problem von Detached Entities in Verbindung mit Lazy Loa-ding zu vermeiden, ist daher die Verwendung von entsprechenden Queries (siehe Kapitel 7.2.6 und Kapitel 8), in denen sichergestellt ist, dass alle benötigten Daten in einer einzi-gen Query geladen werden.

5.4 Callback-Methoden und Entity ListenerMit JPA ist es möglich, Callback-Methoden zu definieren, die bei einer Änderung des Lebenszyklus der Entity aufgerufen werden. Die Methoden können direkt in der Entity oder ausgelagert in speziellen Entity-Listener-Klassen definiert werden.

5.4.1 Beschreibung der Callback-Methoden

Die Callback-Methoden werden durch Annotations als solche markiert. Dabei können die im Folgenden beschriebenen Annotations sowohl direkt in Entities als auch in Entity-Listener-Klassen verwendet werden.

� @PrePersist

� @PostPersist

� @PreRemove

� @PostRemove

� @PreUpdate

� @PostUpdate

� @PostLoad

In Listing 5.6 wird die Verwendung von Callback-Methoden am Beispiel der Entity Logingezeigt. Neben den nötigen Änderungen an der Entity und dem verwendeten Testcode wird auch die Ausgabe auf Standard-Output im Listing aufgeführt.

@Entitypublic class Login implements Serializable { ... @PrePersist @PreUpdate public void executePrePersist() { System.out.println("2 - in @PrePersist"); }

// nur eine Annotation pro Typ pro Entity // @PrePersist // public void notAllowed() {

Listing 5.6: Verwendung von Callback-Methoden in der Entity „Login“

Page 120: Jpa Mit en

Callback-Methoden und Entity Listener

JPA mit Hibernate 119

Anhand des Beispielcodes sollen nun noch wichtige Punkte zu Callback-Methoden erläutert werden. Die Signatur von Callback-Methoden muss immer void <frei wählba-rer Methodenname>() sein. Es können public, private, protected und package als Access-Modifikatoren angegeben werden. Lediglich auf static und final muss man bei der Definition der Callback-Methoden verzichten. Es ist weiterhin zu beachten, dass eine Callback Methode beim Werfen einer Runtime Exception ein Rollback der gesamten Transaktion auslöst.

// System.out.println("NOT ALLOWED"); // }

@PostPersist void executePostPersist() { System.out.println("4 - in PostPersist"); } ...}

@Testpublic void testCallBackMethodPersist () { EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager(); em.getTransaction().begin(); User user = new User("Max", "Muster", "[email protected]"); Login login = new Login(user, "max.muster", "geheim"); System.out.println("1 - vor Aufruf em.persist"); em.persist(login); System.out.println("3 - nach Aufruf em.persist"); em.getTransaction().commit(); System.out.println("5 - nach Transaction commit"); em.close();}

------------- Standard Output ---------------1 - vor Aufruf em.persist2 - in PrePersistHibernate: select nextval ('hibernate_sequence')3 - nach Aufruf em.persistHibernate: insert into T_USER (user_city, user_street, addressDefault_id ...Hibernate: insert into Login (loginName, password, id) values (?, ?, ?)4 - in PostPersist5 - nach Transaction commit

Listing 5.6: Verwendung von Callback-Methoden in der Entity „Login“ (Forts.)

Page 121: Jpa Mit en

5 – Lebenszyklus einer Entity

120

In einer einzelnen Klasse, ob Entity oder Entity Listener, kann jede Annotation nur ein-mal verwendet werden, jedoch kann man eine Methode mit mehreren Annotations mar-kieren.

In den Callback-Methoden sollten keine EntityManager oder Query-Operationen aufge-rufen werden. Ebenso ist die Verwendung anderer Entities oder die Veränderung der Beziehungen zwischen Entities in Bezug auf die Portabilität der Anwendung nicht rat-sam, da dieses Verhalten aktuell noch nicht konkret spezifiziert ist.

Die @PrePersist- und @PreRemove-Methoden werden direkt vor dem Aufruf von persist()bzw. remove() ausgeführt. Die @PostPersist- und @PostRemove-Methoden werden erst nach dem konkreten Persistieren bzw. Löschen in der Datenbank ausgeführt. Im vorlie-genden Beispiel war dies erst mit dem Commit der Transaktion der Fall. Je nach Kaska-dierungsoption werden für die referenzierten Entities ebenfalls Callback-Methoden auf-gerufen, sofern diese definiert wurden.

Für die @PreUpdate-, @PostUpdate- und @PostLoad-Callback-Methoden wurde in Listing 5.7ein erweitertes Beispiel in Anlehnung an Listing 5.6 entwickelt.

@Entitypublic class Login implements Serializable { ... // Hinweis: @PreUpdate für Methode executePrePersist wurde entfernt @PreUpdate void executePreUpdate() { System.out.println("6 - in PreUpdate"); }

@PostUpdate void executePostUpdate() { System.out.println("7 - in PostUpdate"); }

@PostLoad void executePostLoad() { System.out.println("2 - in PostLoad"); } ...}

@Testpublic void testCallBackMethodLoadAndUpdate() { Query query = em.createQuery("SELECT l " + "FROM Login l WHERE l.loginName='max.muster'");

Listing 5.7: Erweitertes Beispiel für Callback Methoden der Entity „Login“

Page 122: Jpa Mit en

Callback-Methoden und Entity Listener

JPA mit Hibernate 121

Die @PostLoad-Methode wird nach dem Laden der Entity in den aktuellen Persistenzkon-text und nach einem Aktualisieren der Entity ausgeführt. Dabei wird die Methode selbst noch vor der Rückgabe der Ergebnismenge aufgerufen.

Im obigen Beispiel wurden aus dem EntityManager mit der Methode clear() alle zuge-hörigen Entities entfernt, dadurch wird beim Aufruf der Methode merge() die Entity erneut geladen, und somit wird die @PostLoad-Callback-Methode zum zweiten Mal aus-geführt.

System.out.println("1 - vor Query"); List<Login> logins = (List<Login>)query.getResultList(); System.out.println("3 - nach Query"); em.clear(); // EM leeren um Mergeverhalten besser zu demonstrieren Login login = logins.get(0); login.setLoginName("muster.max"); em.getTransaction().begin(); System.out.println("4 - vor Aufruf em.merge"); em.merge(login); System.out.println("5 - nach Aufruf em.merge"); em.getTransaction().commit(); System.out.println("8 - nach Transaction commit"); em.close();}

------------- Standard Output ---------------1 - vor QueryHibernate: select login0_.id as id13_, login0_.loginName as loginName13_, loginHibernate: select user0_.id as id10_5_, user0_.user_city as user2_10_5_,...Hibernate: select addresspke0_.id as id11_5_, addresspke0_.city as city11_5_,...2 - in PostLoad3 - nach Query4 - vor Aufruf em.mergeHibernate: select login0_.id as id13_0_, login0_.loginName as loginName13_0_,...Hibernate: select user0_.id as id10_0_, user0_.user_city as user2_10_0_,...Hibernate: select addresspke0_.id as id11_0_, addresspke0_.city as city11_0_,...2 - in PostLoad5 - nach Aufruf em.merge6 - in PreUpdateHibernate: update Login set loginName=?, password=? where id=?7 - in PostUpdate8 - nach Transaction commit

Listing 5.7: Erweitertes Beispiel für Callback Methoden der Entity „Login“ (Forts.)

Page 123: Jpa Mit en

5 – Lebenszyklus einer Entity

122

Die @PreUpdate-Callback-Methode wird direkt vor dem Datenbankupdate ausgeführt und nicht, wie bei @PrePersist, vor dem Aufruf der entsprechenden EntityManager-Methode. Der Aufruf von @PostUpdate findet analog dazu direkt nach dem Datenbankup-date statt. Wie im vorherigen Beispiel, erfolgte die Ausführung der Datenbankstate-ments erst mit dem Commit der Transaktion.

Wird in einer Transaktion nach einem Aufruf von persist() dieselbe Entity nochmals verändert und mit merge() gespeichert, werden sowohl die @PrePersist- und @PostPer-sist- als auch die @PreUpdate- und @PostUpdate-Callback-Methoden ausgeführt. Bei einem Aufruf von merge() und remove() in derselben Transaktion werden allerdings lediglich die @PreRemove- und die @PostRemove-Methoden ausgeführt. Dieses Verhalten ist jedoch rein Hibernate-spezifisch und nicht in der JPA-Spezifikation definiert. Andere Imple-mentierungen können sich hier anders und sogar konträr verhalten.1

5.4.2 Verwendung von Entity-Listener-Klassen

Entity Listener sind Klassen, die spezielle Callback-Methoden enthalten. Mithilfe der Annotation @EntityListeners({Listener1.class, Listener2.class,…}) können einer Entity die benötigten Entity-Listener-Klassen bekannt gemacht werden, wobei die Reihenfolge der Definition der Reihenfolge der Ausführung entspricht.

1. JSR 303, http://jcp.org/en/jsr/detail?id=303

Neu in JPA 2.0

Mit JSR 3031 wurde für Java ein einheitliches API zur Validierung von Objek-ten eingeführt. Es stehen Annotationen wie @NotNull, @Max oder @Size zur Ver-fügung, um Bedigungen für Attribute eines Objekts zu definieren.

Das Validieren von Entities kann ab JPA 2.0 automatisch nach den Callback-Methoden @PrePersist, @PreUpdate und @PreRemove vorgenommen werden. Zur Konfiguration stehen neue Elemente für die persistence.xml zur Verfügung:

� validation-mode: es können die Werte AUTO, CALLBACK oder NONE übergeben werden. Bei CALLBACK muss eine Validierung stattfinden, ansonsten wird eine PersistenceException geworfen. Die Einstellung AUTO ist der Default. Es wird versucht, einen Bean Validation Provider zu initialisieren und die Validierung durchzuführen. Kann kein entsprechender Provider gefun-den werden, wird ohne Fehler fortgefahren. Wird NONE übergeben, findet keine Validierung statt.

� javax.persistence.validation.group.pre-persist, javax.persistence.validation.group.pre-update und javax.persistence.validation.group.pre-remove

Es können für die entsprechenden Callback-Methoden Validierungsgrup-pen angegeben werden. Für @PrePersist und @PreUpdate wird, sofern nicht anders definiert, immer die Default-Validierung ausgeführt. Beim Call-back @PreRemove wird standardmäßig keine Überprüfung ausgeführt.

i

Page 124: Jpa Mit en

Callback-Methoden und Entity Listener

JPA mit Hibernate 123

In Listing 5.8 wird die Verwendung einer Entity-Listener-Klasse für die Entity Login vor-gestellt.

Eine Entity-Listener-Klasse benötigt immer einen Default-Konstruktor mit dem Access-Modifikator public. Die Callback-Methoden müssen der Signatur void <frei wählbarer Methodenname>(Object) entsprechen, wobei der Parameter entsprechend typisiert sein kann, wie dies im vorliegenden Beispiel gezeigt wurde. Der Zustand eines Entity Liste-ners wird nicht persistiert.

5.4.3 Default Entity Listener

Möchte man Default Entity Listener definieren, die allgemein für alle Entities gelten, muss man diese in einem XML-Deskriptor angeben. In Listing 5.9 werden die angepasste persistence.xml und die Datei orm.xml als XML-Diskriminator gezeigt.

public class LoginListener { public LoginListener() {}

@PostPersist public void executePostPersistInLoginListener(Login login) { System.out.println("3 - in PostPersist des LoginListeners" + " für Entity " + login.toString()); }}

@Entity@EntityListeners(LoginListener.class)public class Login implements Serializable { ...}

Listing 5.8: Definition eines EntityListeners für die Entity „Login“

<!-- persistence.xml --><?xml version="1.0" encoding="UTF-8"?> ... <provider>org.hibernate.ejb.HibernatePersistence</provider> <mapping-file>META-INF/orm.xml</mapping-file> <properties> ...

<!-- orm.xml --><entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

Listing 5.9: Mapping-Datei orm.xml und angepasste persistence.xml

Page 125: Jpa Mit en

5 – Lebenszyklus einer Entity

124

In der persistence.xml wird das Mappingfile mit dem Tag mapping-file angegeben. Dabei ist zu beachten, dass das Tag nach provider aufgeführt werden muss, da es sonst zu einer Exception kommt. In der Datei orm.xml können nun im Tag entity-listeners die einzelnen Klassen angegeben werden, die als Default Entity Listener verwendet werden sollen. Mit den Tags pre-persist, post-persist, pre-remove, post-remove, pre-update, post-update und post-load werden in Analogie zu den Annotations die Callback-Methoden angegeben.

In Listing 5.10 wird die Klasse DefaultListener gezeigt, die als Default Entity Listener Ver-wendung findet. Neben dem zwingend erforderlichen Default-Konstruktor braucht man nichts weiter zu beachten.

5.4.4 Ausführungsreihenfolge gleicher Callback-Methoden

Zwar kann pro einzelner Klasse (Entity, Entity Listener und Default Entity Listener) jeweils nur eine Callback-Methode des gleichen Typs definiert werden, jedoch ist es in der Gesamtheit nichtsdestotrotz möglich, dass für eine Zustandsänderung einer Entity mehrere Callback-Methoden ausgeführt werden. Dabei gilt folgende Reihenfolge: Zuerst wird die Callback-Methode des Default Entity Listeners und danach die für den Entity definierte Entity Listener ausgeführt. Zum Schluss kommt die Callback-Methode in der Entity an die Reihe. In Listing 5.11 wird der Standard-Output gezeigt, der beim persistie-ren einer Entity Login erzeugt wird. Dabei wird davon ausgegangen, dass die in den obi-gen Abschnitten definierten Callback-Methoden und Listener vorhanden sind.

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ... > <persistence-unit-metadata> <persistence-unit-defaults> <entity-listeners> <entity-listener class="booksonline.listener.DefaultListener"> <post-persist method-name="executePostPersist"/> </entity-listener> </entity-listeners> </persistence-unit-defaults> </persistence-unit-metadata></entity-mappings>

public class DefaultListener { public DefaultListener() {} public void executePostPersist(Object obj) { System.out.println("2 - in PostPersist des DefaultListeners" + " für Entity " + obj.toString()); }}

Listing 5.10: Definition des Default Entity Listeners

Listing 5.9: Mapping-Datei orm.xml und angepasste persistence.xml (Forts.)

Page 126: Jpa Mit en

Zusammenfassung

JPA mit Hibernate 125

5.5 ZusammenfassungIn diesem Kapitel wurde der Lebenszyklus einer Entity vorgestellt. Da eine Entity nur wenige Zustäde annehmen kann, bleibt die Übersichtlichkeit stets gewahrt. Mit dem EntityManager existiert eine zentrale Stelle zur Verwaltung der Entities und deren Zuständen. Mit den Callback-Methoden gibt es eine Möglichkeit, Methoden einer Entity bei Zustandsübergängen aufzurufen.

Die Verwendung von Detached Entities bietet eine Reihe von Vorteilen, so müssen z. B. keine extra Transferobjekte für die Übertragung zwischen Server und Client definiert werden. Detached Entities bieten allerdings auch einige Fallstricke, die ebenfalls erläu-tert wurden.

...Hibernate: insert into Login (loginName, password, id) values (?, ?, ?)2 - in PostPersist des DefaultListeners für Entity booksonline.bo.Login[id=1]3 - in PostPersist des LoginListeners für Entity booksonline.bo.Login[id=1]4 - in PostPersist

Listing 5.11: Beispiel zur Ausführungsreihenfolge von Callback-Methoden

Page 127: Jpa Mit en
Page 128: Jpa Mit en

JPA mit Hibernate 127

6 Transaktionen, EntityManager und Persistenzkontext

In diesem Kapitel wird die Verwendung von Datenbanktransaktionen sowie die des EntityManagers und dazugehörigen Persistenzkontexts vorgestellt. Zu diesem Zweck wird im ersten Abschnitt eine kurze Einführung in die Thematik der Transaktionen in heutigen Datenbanksystemen gegeben. In den weiteren Abschnitten werden die Locking-Mechanismen von JPA und Hibernate betrachtet. Darüber hinaus werden noch verschiedene Anwendungsszenarien (Patterns) näher vorgestellt.

6.1 Transaktionen

6.1.1 Was ist eine Transaktion?

Eine Transaktion im Kontext von Datenbanken lässt sich am besten mit den so genannten ACID-Eigenschaften definieren:

� Atomic (atomar): Eine Transaktion besteht normalerweise aus einer Reihe von „primitiven“ Operationen, also mehreren Änderungen von Tabelleninhalten. Diese Änderungen werden durch die Transaktion, die diese Operationen umschließt, zu einer atomaren Einheit zusammengefasst. Das heißt, eine Transaktion ist unteilbar: Es werden entweder alle Operationen innerhalb einer Transaktion ausgeführt oder gar keine.

� Consistent (konsistent): Die Änderungen, die eine Transaktion am Inhalt einer Daten-bank durchführt, hinterlassen die Datenbank, bzw. den Tabelleninhalt, immer in einem konsistenten Zustand. Das heißt, die Daten dürfen sich nach der Durchfüh-rung einer Transaktion nicht in einem widersprüchlichen Zustand befinden, natür-lich vorausgesetzt, die Daten befanden sich zuvor in einem widerspruchsfreien Zustand.

� Isolated (isoliert): Werden mehrere Transaktionen gleichzeitig ausgeführt, dürfen sie sich in keiner Weise beeinflussen. Betreffen die Änderungen zweier Transaktionen verschiedene Datensätze, ist das normalerweise auch kein Problem. Erst wenn mehrere Transaktionen Änderungen an den gleichen Datensätzen durchführen, kann es zu so genannten Seiteneffekten kommen. Diese Seiteneffekte führen sehr schnell zu inkonsistenten Zuständen und müssen darum vermieden werden.

� Durable (dauerhaft): Wird eine Transaktion abgeschlossen, müssen die Änderungen am Datenbestand dauerhaft sein. Das heißt, es darf nicht vorkommen, dass die Auswirkungen einer Transaktion nach deren erfolgreicher Beendigung aus irgend-welchen Gründen verloren gehen.

Page 129: Jpa Mit en

6 – Transaktionen, EntityManager und Persistenzkontext

128

Die einfachste und sicherste Möglichkeit, die obigen Kriterien aus Sicht der Datenbank zu erfüllen, wäre eine einfache Serialisierung der Transaktionen, also die Ausführung der Transaktionen nacheinander. Diese Lösung ist aber leider auch die langsamste und kommt deshalb in der Praxis nie zum Einsatz, denn alle Datenbanken sind darauf ausge-legt, möglichst viele Transaktionen in kürzester Zeit auszuführen. Deshalb kommen in modernen Datenbanksystemen teils sehr komplexe Methoden zum Einsatz, deren Erläu-terung den Umfang dieses Buches bei weitem sprengen würde. Aus Sicht des Entwick-lers spielt es aber keine so große Rolle, wie die Datenbank Transaktionen umsetzt, son-dern mehr, wie sie sinnvoll in der eigenen Anwendung verwendet werden können.

Transaktionen können immer auf zwei verschiedene Arten beendet werden:

� commit: Die erfolgreiche Beendigung einer Transaktion. Nach einem Commit wer-den die Änderungen sofort dauerhaft in der Datenbank abgespeichert und sind ab diesem Zeitpunkt für alle sichtbar.

� rollback: Der Abbruch einer Transaktion. Eine Transaktion, die noch nicht durch ein Commit beendet wurde, kann jederzeit durch ein Rollback abgebrochen werden. Alle Änderungen am Datenbestand, die innerhalb dieser Transaktion durchgeführt wur-den, sind dadurch gegenstandslos, das heißt, der Zustand der Daten ist so, als wären keine Operationen durchgeführt worden.

6.1.2 Isolationsebenen

Wie bereits erwähnt, bedeutet eine komplette Serialisierung der transaktionalen Opera-tionen eine erhebliche Einschränkung für parallel laufende Zugriffe auf die Datenbank. Aus diesem Grund führt man so genannte Isolationsebenen ein, innerhalb derer bestimmte Einschränkungen im Vergleich zu idealen ACID-Transaktionen in Kauf genommen werden.

Grundsätzlich können folgende Probleme bei parallelen Datenbankoperationen auftreten:

� Dirty Read: Innerhalb einer Transaktion (T1) wird ein Datensatz verändert. Dieser veränderte Datensatz wird innerhalb einer zweiten Transaktion (T2) gelesen, bevor T1 abgeschlossen wurde (Commit). Wird nun T1 mit einem Rollback abgebrochen, arbeiten die Operationen innerhalb von T2 mit einem ungültigen Wert.

� Non-Repeatable Read: Innerhalb einer Transaktion (T1) wird ein bestimmter Daten-satz gelesen. Direkt nach dem Lesen von T1, aber noch vor einem Commit von T1, verändert eine zweite Transaktion (T2) diesen Datensatz und wird mit einem Commit beendet. Liest nun T1 diesen Datensatz erneut, wird ein anderer Inhalt zurückgege-ben als zu Beginn, obwohl aus der Sicht von T1 der Datensatz nicht verändert wurde.

� Phantom Read: Innerhalb einer Transaktion (T1) wird eine Abfrage an die Datenbank gestellt, die eine bestimmte Anzahl von Ergebnisdatensätzen liefert. Eine zweite Transaktion (T2) ändert den Inhalt der Datenbank, indem sie neue Datensätze ein-fügt, und wird mit einem Commit beendet. Führt nun T1 die gleiche Abfrage erneut aus, werden mehr Ergebnisdatensätze als beim ersten Mal gefunden.

Page 130: Jpa Mit en

Transaktionen

JPA mit Hibernate 129

In Tabelle 6.1 ist dargestellt, welche Probleme innerhalb der verschiedenen Isolationsebe-nen (nach ANSI-92 SQL) auftreten können:

Wie anhand der Tabelle sehr gut zu erkennen ist, nähern sich die Transaktionen mit zunehmendem Isolationsgrad der idealen Transaktion an. In der Isolationsebene Seria-lizable können keine der hier vorgestellten Probleme mehr auftreten.

Mit zunehmendem Isolationsgrad nimmt aber auch die Performance des Datenbank-systems bei gleichzeitig laufenden Transaktionen deutlich ab, da mehr Sperren innerhalb der Datenbank gesetzt werden müssen, um die parallele Nutzung von Datensätzen durch verschiedene Transaktionen zu verhindern.

Die verschiedenen Isolationsebenen werden nicht komplett in allen Datenbanken unter-stützt. In der Dokumentation jeder Datenbank sollte aber exakt beschrieben sein, welche Isolationsebenen angeboten werden. Außerdem bietet z. B. JDBC über die Klasse java.sql.DatabaseMetaData die Möglichkeit, zur Laufzeit herauszufinden, welche Isola-tionsebenen von der Datenbank angeboten werden.

6.1.3 Transaktionssteuerung in JPA

Mit JPA gibt es zwei grundlegende Möglichkeiten der Transaktionssteuerung. Zum einen wird die JTA (Java Transaction API) unterstützt, zum anderen können die Transak-tionen mittels des EntityTransaction-Interfaces selbst von der Anwendung kontrolliert werden. Um dies zu erreichen, muss bei der Angabe des Transaktionstyps im Gegensatz zu JTA Resource.Local angegeben werden.

Die Art der Transaktionssteuerung wird mit der Erzeugung des EntityManagers spezifi-ziert. In Kapitel 6.3.2 wird ausführlich auf die unterschiedlichen Arten der Erzeugung von EntityManagern in Java-EE- und Java-SE-Umgebungen eingegangen.

JTA-Transaktionen

Bei der Verwendung von JTA nimmt der EntityManager an bereits existierenden Trans-aktionen teil. Diese Transaktionen werden unabhängig vom EntityManager erzeugt und geschlossen. Mit der Methode joinTransaction() des EntityManagers kann der Entity-Manager an eine bereits bestehende JTA-Transaktion gebunden werden, wenn er außer-halb einer aktiven Transaktion erzeugt wurde. Die Methode wirft eine Transaction-RequiredException, sofern aktuell keine entsprechende Transaktion existiert.

Isolationsebene Dirty Read Non Repeatable Read Phantom Read

Read Uncommitted Möglich Möglich Möglich

Read Committed Nicht möglich Möglich Möglich

Repeatable Read Nicht möglich Nicht möglich Möglich

Serializable Nicht möglich Nicht möglich Nicht möglich

Tabelle 6.1: Mögliche Probleme bei verschiedenen Isolationsebenen

Page 131: Jpa Mit en

6 – Transaktionen, EntityManager und Persistenzkontext

130

Resource-Local-Transaktionen

Bei der direkten Steuerung der Transaktionen in der Anwendung kommt das EntityTransaction-Interface zum Einsatz. Eine EntityTransaction kann man mittels des EntityManagers mit der Methode getTransaction() erhalten. Sollte die Methode get-Transaction() auf einen EntityManger aufgerufen werden, der für die Verwendung von JTA konfiguriert ist, wird eine IllegalStateException geworfen.

Die folgende Auflistung zeigt die Methoden der EntityTransaction und stellt diese kurz vor:

� void begin(): Die Methode startet die Transaktion. Sollte die entsprechende Transak-tion bereits aktiv sein (isActive() == true), wird eine IllegalStateException geworfen.

� void commit(): Die Methode führt ein Commit der Transaktion aus, wobei alle Ände-rungen der Transaktion endgültig in die Datenbank übernommen werden.

� void rollback(): Die Methode führt ein Rollback der Transaktion aus. Alle In der Transaktion durchgeführten Datenbankänderungen werden verworfen.

� void setRollbackOnly(): Die aktuelle Transaktion wird mit einem Flag markiert, sodass sie auf jeden Fall mit einem Rollback beendet werden muss. Wird auf eine Transaktion mit einem aktiven RollbackOnly-Flag ein commit() ausgeführt, wird eine Rollback-Exception geworfen. Die Methode kann nur auf einer aktiven Transaktion ausgeführt werden (isActive() == true), da sonst eine IllegalStateException geworfen wird.

� boolean getRollbackOnly(): Gibt den Status des RollbackOnly-Flags zurück. Sollte die Transaktion nicht aktiv sein, wird ebenfalls eine IllegalStateException geworfen.

� boolean isActive(): Gibt den Status der Transaktion zurück, ob diese aktiv ist oder nicht.

6.2 LockingIm Beispiel in Listing 6.1 wird die Problematik des „Lost Update“ gezeigt. Zwei Transak-tionen bearbeiten dabei parallel die gleiche Entity. Im Beispiel wird dieses Verhalten künstlich durch eine serielle Abarbeitung herbeigeführt. In der Praxis tritt dieses Pro-blem vor allem bei Multiuser-Anwendungen und der damit verbundenen Verwendung von mehreren Threads auf.

public class UserTest {...@Test public void testLostUpdate() {

// Entity anlegen und in Datenbank speichern EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager(); em.getTransaction().begin();

Listing 6.1: Lost-Update-Problematik

Page 132: Jpa Mit en

Locking

JPA mit Hibernate 131

User user = new User("Lost", "Update", "[email protected]"); em.persist(user); em.getTransaction().commit(); em.close();

// Entity mit EntityManager em1 aus Datenbank laden EntityManager em1 = JpaUtil.getEntityManagerFactory().createEntityManager(); Query query1 = em1.createQuery("SELECT u " + "FROM User u WHERE u.email='[email protected]'"); User user1 = (User)query1.getSingleResult();

// Entity mit EntityManager em2 aus Datenbank laden EntityManager em2 = JpaUtil.getEntityManagerFactory().createEntityManager(); Query query2 = em2.createQuery("SELECT u " + "FROM User u WHERE u.email='[email protected]'"); User user2 = (User)query2.getSingleResult();

// Prüfen ob es sich wirklich um die gleichen Entites handlet assertEquals(user1.getId(), user2.getId());

// Ändern des Usernamens in Transaktion t1 EntityTransaction t1 = em1.getTransaction(); t1.begin(); user1.setFirstname("EINS"); em1.merge(user1);

// Ändern des Usernamens in Transaktion t2 EntityTransaction t2 = em2.getTransaction(); t2.begin(); user2.setFirstname("ZWEI"); em2.merge(user2);

// Beenden der Transaktion t2 t2.commit();

// Beenden der Transaktion t1 t1.commit();

//Überprüfen des Usernamens von user 2 vor und nach dem Refresh System.out.println("Vor Refresh: " + user2.getFirstname());

Listing 6.1: Lost-Update-Problematik (Forts.)

Page 133: Jpa Mit en

6 – Transaktionen, EntityManager und Persistenzkontext

132

In den Transaktionen t1 und t2 wird jeweils eine Änderung an der Entity user1 bzw. user2vorgenommen, die die gleiche ID haben und somit identisch sind. Im Beispiel werden die Änderungen der Transaktion t2 durch die Änderungen der Transaktion t1 überschrieben.

Dieses Verhalten wird auch Last Commit Wins genannt, da die Änderung, welche als Letztes abgespeichert wird, alle vorhergehenden Änderungen überschreibt.

Es gibt drei Arten, wie mit dem Problem umgegangen werden kann. Das Einfachste ist es, nichts zu tun, wobei es aber zu Verwirrung seitens der Benutzer kommen kann, wodurch diese „Lösung“ des Problems nur in wenigen Fällen sinnvoll ist. Die beiden anderen Lösungsansätze der Lost-Update-Problematik sind optimistisches und pessimistisches Locking.

Optimistisches Locking ist das Mittel der Wahl, um der Forderung nach möglichst hoher Performance und guter Skalierbarkeit gerecht zu werden. Prinzipiell wird dabei ange-nommen, dass alles gut geht, daher auch der Begriff „optimistisches“ Locking. Mit „alles geht gut“ ist dabei gemeint, dass es zu keinen Überschneidungen von Änderungen durch parallele Transaktionen kommt. Für den Fall, dass es doch zu solchen Überschneidungen kommt, muss sichergestellt sein, dass dies erkannt und gesondert behandelt wird. Opti-mistisches Locking bedeutet in diesem Fall, dass erst nach dem Auftreten einer Über-schneidung eine quasi manuelle Korrektur erfolgt, die dann auch aufwändiger als bei einem pessimistischen Ansatz ist.

em2.refresh(user2); System.out.println("Nach Refresh: " + user2.getFirstname());

//Überprüfen des Usernamens in der Datenbank em = JpaUtil.getEntityManagerFactory().createEntityManager(); Query query = em.createQuery("SELECT u " + "FROM User u WHERE u.email='[email protected]'"); System.out.println("In der DB: " + ((User)query.getSingleResult()).getFirstname());

// Aufräumen em.close(); em1.close(); em2.close(); }}

Output:Vor Refresh: ZWEINach Refresh: EINSIn der DB: EINS

Listing 6.1: Lost-Update-Problematik (Forts.)

Page 134: Jpa Mit en

Locking

JPA mit Hibernate 133

Pessimistisches Locking geht von häufigen Kollisionen zwischen konkurrierenden Transaktionen aus. Aus diesem Grund wird bei pessimistischem Locking ein sehr rest-riktives Vorgehen verwendet. Tabellenzeilen werden explizit gesperrt, noch bevor eine Überlappung stattfinden kann. Durch dieses Vorgehen wird sichergestellt, dass es nicht zu nachträglichen Konflikten wie beim optimistischen Locking kommen kann. Sollte eine konkurrierende Transaktion versuchen, die gelockten Zeilen ebenfalls zu verwen-den, erhält sie umgehend eine Fehlermeldung oder sie muss warten, bis die Zeilen wie-der freigegeben worden sind. Dieses Vorgehen verschlechtert natürlich die Gesamtper-formance eines Systems aufgrund der wartenden Transaktionen erheblich und sollte deshalb nur wohlbedacht eingesetzt werden.

In JPA kann sowohl optimistisches als auch pessimistisches Locking zum Einsatz kom-men. Darüber hinaus kann man mit Hibernate weitere Einstellungen bezüglich des opti-mistischen Lockings vornehmen, die aktuell nicht in der JPA-Spezifikation definiert sind. In den folgenden Abschnitten werden die verschiedenen Möglichkeiten vorgestellt.

6.2.1 Optimistisches Locking

Optimistisches Locking wird in JPA mittels Versionierung umgesetzt. Dabei bekommt jede Entity ein Versionsattribut, das mit der Annotation @Version versehen wird. Als mögliche Datentypen kommen dafür int, Integer, short, Short, long, Long und Timestamp in Frage. In Listing 6.2 wird die Entity UserWithVersion gezeigt, die ein entsprechendes Versi-onsattribut besitzt. Dadurch wird in JPA automatisch optimistisches Locking für die Entity aktiviert.

Das Versionsattribut wird bei einem Update mit dem Wert in der Datenbank verglichen und bei der Durchführung des Updates automatisch durch den EntityManager erhöht. Stimmt die Version einer Entity nicht mit dem Wert in der Datenbank überein, so bedeu-tet das, dass die Entity durch einen parallelen Zugriff bereits verändert wurde. In diesem Fall wird eine OptimisticLockException ausgelöst.

@Entitypublic class UserWithVersion implements Serializable {

@Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstname; private String lastname; private String email; @Version private Long version; ...

Listing 6.2: Entity UserWithVersion

Page 135: Jpa Mit en

6 – Transaktionen, EntityManager und Persistenzkontext

134

An dieser Stelle besteht nun die Möglichkeit, auf den Konflikt zu reagieren. Dies kann beispielsweise durch eine Mitteilung an den Benutzer der Anwendung geschehen, dass die Daten seit dem letzten Auslesen verändert wurden und er seine Eingaben überprü-fen soll.

Auf das Versionsattribut kann lesend zugegriffen werden, um die Versionsinformation der Entity zu verarbeiten. Ein schreibender Zugriff ist jedoch nicht erlaubt, da lediglich der Persistenzprovider den Wert der Version setzt, sobald die Entity in der Datenbank gespeichert wird.

Das Beispiel aus Kapitel 6.2, in dem die Lost-Update-Problematik gezeigt wurde, wird mit der neuen Klasse UserWithVersion versehen, um das beschriebene Verhalten zu demonstrieren. In Listing 6.3 wird die Veränderung zu der Testmethode testLostUpdategezeigt.

@Test public void testOptomisticLocking() { ... UserWithVersion user = new UserWithVersion("Optimistic", "Lock", "[email protected]"); ...

// Entity mit EntityManager em1 aus Datenbank laden EntityManager em1 = JpaUtil.getEntityManagerFactory().createEntityManager(); Query query1 = em1.createQuery("SELECT u " + "FROM UserWithVersion u WHERE u.email='[email protected]'"); UserWithVersion user1 = (UserWithVersion)query1.getSingleResult();

// Entity mit EntityManager em2 aus Datenbank laden EntityManager em2 = JpaUtil.getEntityManagerFactory().createEntityManager(); Query query2 = em2.createQuery("SELECT u " + "FROM UserWithVersion u WHERE u.email='[email protected]'"); UserWithVersion user2 = (UserWithVersion)query2.getSingleResult(); // Prüfen ob es sich wirklich um die gleichen Entites handlet ...

// Beenden der Transaktion t2 t2.commit();

// Beenden der Transaktion t1 führt zu einer Exception try{ t1.commit(); fail();

Listing 6.3: Test für Optimistic Locking

Page 136: Jpa Mit en

Locking

JPA mit Hibernate 135

Die Daten und deren Änderungen sind analog zu denen aus Listing 6.1. Jedoch wird beim Versuch, das commit auf Transaktion t1 auszuführen, eine Exception geworfen. Im konkreten Beispiel ist das eine RollbackException, die die OptimisticLockException kapselt. In Listing 6.4 ist der entsprechende Stacktrace dargestellt, der dieses Verhalten von Hibernate aufzeigt.

} catch (RollbackException e) { // beheben der Dateninkonsistenz }

//Überprüfen des Usernamens von user 2 vor und nach dem Refresh System.out.println("Vor Refresh: " + user2.getFirstname()); em2.refresh(user2); System.out.println("Nach Refresh: " + user2.getFirstname());

//Überprüfen des Usernamens in der Datenbank em = JpaUtil.getEntityManagerFactory().createEntityManager(); Query query = em.createQuery("SELECT u " + "FROM UserWithVersion u WHERE u.email='[email protected]'"); System.out.println("In der DB: " + ((UserWithVersion)query.getSingleResult()).getFirstname());

// Aufräumen ...Output:Vor Refresh: ZWEINach Refresh: ZWEIIn der DB: ZWEI

javax.persistence.RollbackException: Error while commiting the transaction at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:71) at booksonline.test.UserTest.testOptomisticLocking(UserTest.java:214)

Caused by: javax.persistence.OptimisticLockException at org.hibernate.ejb.AbstractEntityManagerImpl.wrapStaleStateException( AbstractEntityManagerImpl.java:650) at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:59)

Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect):

Listing 6.4: Kapselung der OptimisticLockException

Listing 6.3: Test für Optimistic Locking (Forts.)

Page 137: Jpa Mit en

6 – Transaktionen, EntityManager und Persistenzkontext

136

6.2.2 Lock-Modi von JPA und Hibernate

In den meisten Anwendungsfällen sollte das optimistische Locking mittels des Versions-attributs ausreichen. Allerdings kann es in bestimmten Fällen notwendig werden , auf einen restriktiveren Locking-Mechanismus zurückzugreifen, um bei häufigen Kollisio-nen direkt durch Lock-Modi Datenbank-Locks zu setzen.

In JPA der Version 1.0 sind in der Klasse LockModeType zwei Lock-Modi definiert, die in der Methode EntityManager.lock() verwendet werden können:

� LockModeType.READ: Es wird mit SELECT ... FOR UPDATE ein Lock auf die Tabellenzeilen gesetzt, wenn die Datenbank dieses Feature unterstützt. Allerdings muss der Lock auf der Datenbankzeile nicht sofort erfolgen, sondern kann auch erst spätestens mit dem Commit durchgeführt werden. Dieser Lock-Modus ist identisch mit Lock-Mode.Upgrade von Hibernate.

� LockModeType.WRITE: In diesem Lock-Modus wird das Versionsattribut der Entity erhöht unabhängig davon, ob die Entity verändert wurde oder nicht.

[booksonline.bo.UserWithVersion#7] at org.hibernate.persister.entity.AbstractEntityPersister.check( AbstractEntityPersister.java:1769) at org.hibernate.persister.entity.AbstractEntityPersister.update( AbstractEntityPersister.java:2412)...

Neu in JPA 2.0

Mit JPA 2.0 wurden für die Enumeration LockModeType neue Werte einge-führt. Dabei wurden jene für das optimistische Locking lediglich zur besseren Unterscheidung umbenannt:

� OPTIMISTIC: Synonym für LockModeType.READ

� OPTIMISTIC_FORCE_INCREMENT: Synonym für LockModeType.Write

In der JPA-2.0-Spezifikation spricht man von pessimistischem Locking, wenn langandauernde Locks auf den Datenbankzeilen sofort durchgeführt werden. Es werden Dirty Read und Non-Repeatable Read verhindert. Es stehen dafür drei neue Modi zur Verfügung:

� PESSIMISTIC_READ: Für das Locken der Entity und deren zugehörigen Datenbankzeile(n) zum Lesen, ohne aber anderen Transaktionen das Lesen der Entity zu verwehren.

Listing 6.4: Kapselung der OptimisticLockException (Forts.)

i

Page 138: Jpa Mit en

Locking

JPA mit Hibernate 137

In Hibernate definiert die Klasse LockMode die verschiedenen Lock-Modi, die speziell mit Hibernate verwendet werden können. Diese Lock-Modi können als Parameter den Methoden Session.load(), Session.lock(), Session.get() und Query.setLockMode() explizit gesetzt werden und erlauben somit ein pessimistisches Locking auf einer sehr feingranu-laren Ebene.

Da Hibernate an dieser Stelle ausschließlich die Locking-Funktionalität der Datenbank verwendet, stehen nicht immer alle Modi zur Verfügung. Sollte in einem konkreten Fall ein bestimmter, explizit gewünschter Lock-Modus nicht von der aktuellen Datenbank unterstützt werden, wählt Hibernate automatisch einen verfügbaren Modus aus, der dem gewünschten am nächsten kommt.

Folgende Lock-Modi werden in LockMode definiert:

� LockMode.NONE: Es wird kein Lock auf Zeilen in der Datenbank gesetzt. Mit diesem Modus wird beim Lesen eines Datensatzes nur auf die Datenbank zugegriffen, wenn sich die entsprechende Entity nicht im Cache befindet.

� LockMode.READ: Dieser Lock-Modus weist Hibernate an, direkt auf die Datenbank zuzugreifen und eine Versionsüberprüfung der betroffenen Entities durchzuführen. Sinnvoll z. B. bei Verwendung von Detached Entities. Dieser Lock-Modus wird von Hibernate automatisch verwendet, wenn als Isolationsebene Repeatable Read oder Serializable ausgewählt wurde.

� LockMode.UPGRADE: Ist identisch mit LockModeType.READ von JPA.

Neu in JPA 2.0 (Fortsetzung)

� PESSIMISTIC_WRITE: Es wird ein Schreib-Lock auf die Datenbankzeile(n) der entsprechenden Entitiy ausgeführt, um Datenänderungen verschiedener Transaktionen nacheinander auszuführen. Der Lock-Modus kann aber auch zum Lesen verwendet werden, wenn die hohe Wahrscheinlichkeit besteht, dass andere parallele Transaktionen den Datensatz verändern werden.

� PESSIMISTIC_FORCE_INCREMENT: Es wird das Verisonsattribut der Entity inkrementiert, unabhängig davon, ob Änderungen an der Entity durchge-führt wurden oder nicht.

Beim pessimistischem Locken werden die Datenbankzeilen der Entity gelockt, die alle persistenen Attribute (ohne Collections) enthalten. Dies betrifft auch Datenbanktabellen die aufgrund von Verbungshierarchien oder dem Mappen mittels @SecondaryTable zur Entity gehören. Außerdem werden alle in Bezie-hung stehenden Entities gelockt, für die die zu lockende Entity den Fremd-schlüssel hält. Für alle anderen Bezeihungen sowie alle Collection-Attribute wird im Normalfall kein Lock gesetzt.

� Mit dem Property javax.persistence.lock.scope kann jedoch der Wert Pes-simisticLockScope.EXTENDED an die entsprechenden Methoden des Entity-Managers und der Query übergeben werden. Dadurch werden auch alle in Beziehung stehenden Entites, die über Join-Tables gemappt sind, gelockt.

i

Page 139: Jpa Mit en

6 – Transaktionen, EntityManager und Persistenzkontext

138

� LockMode.UPGRADE_NOWAIT: Verhält sich prinzipiell wie UPGRADE. Durch ein SELECT ... FOR UPDATE NOWAIT, das in Oracle-Datenbanken verfügbar ist, wird die Datenbank angewiesen, nicht darauf zu warten, falls die selektierte Tabellenzeile bereits gelockt ist, sondern stattdessen eine Locking Exception zu werfen.

� LockMode.WRITE: Ein „interner“ Modus, der automatisch von Hibernate verwendet wird, wenn ein Datensatz aktualisiert oder eingefügt wird. Dieser Modus kann nicht explizit durch den User verwendet werden.

Zwar sind sich die lock()-Methoden von Session und EntityManager ähnlich, doch unterscheiden sie sich in einem wesentlichen Aspekt. Mithilfe der Methode Ses-sion.lock() ist es möglich, eine unveränderte Detached Entity wieder an eine Session zu binden. Die Entity, die der Methode EntityManager.lock() übergeben wird, muss sich bereits im Persistenzkontext des EntityManagers befinden, ansonsten wird eine entspre-chende Exception geworfen.

Im Beispiel in Listing 6.5 wird eine Entity vom Typ Book durch Übergabe von Lock-ModeType.READ in der Methode EntityManager.lock() innerhalb der Datenbank gelockt. Verändert jetzt eine parallele Transaktion dieselbe Entity, würde dies durch den Lock in der Datenbank verhindert.

6.2.3 Erweiterte Einstellungen für das Locking in Hibernate

Mit der Annotation @org.hibernate.annotations.Entity und dem darin zu definierenden Parameter optimisticLock kann die Strategie für das optimistische Locking der Entity definiert werden. Die folgenden Möglichkeiten stehen dabei zur Verfügung:

� OptimisticLockType.NONE: Das optimistische Locking wird für die Entity deaktiviert.

� OptimisticLockType.VERSION: Das default Verhalten für die Entity. Es findet eine auto-matische Versionsüberprüfung statt, sofern ein Versionsattribut definiert ist.

� OptimisticLockType.DIRTY: Die Versionsüberprüfung wird nur für veränderte Attri-bute der Entity durchgeführt. Dadurch sind allerdings überlappende Änderungen zwischen den betroffenen Entities möglich.

� OptimisticLockType.ALL: Dieser Parameter veranlasst, dass alle Attribute der Entity zur Überprüfung auf Veränderungen genutzt werden. Dies ist dann sinnvoll, wenn der vorliegenden Entity kein Versionsattribut hinzugefügt werden kann.

Möchte man die Modi ALL oder DIRTY verwenden, muss ebenfalls noch dynamic-update=true in der Annotation @org.hibernate.annotations.Entity angegeben werden.

em.getTransaction().begin(); //Start einer TransaktionBook mybook = em.find(Book.class,1L);em.lock(mybook);mybook.setTitle("JPA und Hibernate");Em.getTransaction().commit();// Ende der Transaktion, Lock wird freigegeben

Listing 6.5: Verwendung von EntityManager.lock()

Page 140: Jpa Mit en

Entity Manager und Persistenzkontext

JPA mit Hibernate 139

In manchen Fällen ist es sinnvoll, dass das Versionsattribut nicht erhöht wird, obwohl sich ein Attribut der Entity verändert hat. Oder, was gleichbedeutend ist, man möchte ein Attribut verändern können, ohne dass ein optimistischer Lock nötig ist.

Dieses Szenario ist vor allem bei der Verwendung von Collections an einer Entity sinn-voll, wenn die Werte der Collection nicht zum Zustand der Entity gezählt werden sollen. Um das soeben beschriebene Verhalten zu erreichen, muss das entsprechende Attribut mit der Annotation @org.hibernate.annotations.OptimisticLock(excluded=true) markiert werden.

6.3 Entity Manager und PersistenzkontextDie Kernkomponenten von JPA sind der Persistenzkontext und der EntityManager. Für das Verständnis der Funktionsweise von JPA ist es erforderlich, deren Zusammenspiel und das unterschiedliche Verhalten in Java-EE- und Java-SE-Umgebungen zu kennen. In den folgenden Abschnitten soll dieses Wissen vermittelt werden.

Ein Persistenzkontext beschreibt eine konkrete Menge von Entities, die durch den Entity-Manager verwaltet werden. Mit dem Entity Manager können Entities persistiert, aktu-alisiert, gesucht und gelöscht werden (Kapitel 3). Jedem Entity Manager ist genau ein Persistenzkontext zugeordnet, und dieser kann demzufolge auch nur die Entities dieses Persistenzkontexts verwalten. Allerdings kann ein und derselbe Persistenzkontext meh-reren EntityManagern zugewiesen werden.

Die mögliche Menge von Entities, die durch den Entity Manager verwaltet wird und sich somit im Persistenzkontext befinden kann, ist durch die persistence unit definiert (beschreibt alle Klassen, deren Beziehungen und das konkrete Mapping auf die relatio-nale Datenbank. Kapitel 4).

Die Begriffe „EntityManger“ und „Persistenzkontext“ werden in der Literatur oftmals synonym verwendet. Dabei ist der EntityManager die konkrete Java-Klasse und der Per-sistenzkontext die Menge der verwalteten Entities.

6.3.1 Arten und Lebenszyklus des Persistenzkontexts

In Java-EE-Anwendungen ist es durchaus üblich, dass in Transaktionen ein komplexer Ablauf abgebildet wird, der es nötig macht, dass auf viele Komponenten zugegriffen werden muss. Dabei ist es natürlich sinnvoll, dass die einzelnen Komponenten jeweils auf den gleichen Persistenzkontext Zugriff haben.

Um dieses Verhalten zu erreichen, wird beim Laden des EntityManagers mittels Depen-dency Injektion oder dem Lookup mittels JNDI der Persistenzkontext automatisch an die aktuelle JTA-Transaktion propagiert. Durch diesen Mechanismus ist es nicht nötig, Refe-renzen des EntityManagers von einer Komponente zur anderen mitzugeben.

Ein durch Java EE verwalteter Persistenzkontext wird auch als container-managed persis-tence context bezeichnet. Das Erstellen und Löschen der zugehörigen EntityManager wird vom Java-EE Container durchgeführt und überwacht.

Page 141: Jpa Mit en

6 – Transaktionen, EntityManager und Persistenzkontext

140

Allerdings kann es bei einigen Anwendungsfällen nötig werden, dass ein unabhängiger Persistenzkontext benötigt wird, der nicht mit der JTA Transaktion propagiert wird. In diesen Fällen wird mit der Erzeugung des EntityManagers ein neuer „frischer“ Persis-tenzkontext erstellt, der nur von dem gerade erstellten EntityManager, und nicht auch von etwaigen anderen EntityManagern zugreifbar ist. Der durch die Anwendung expli-zit erstellte Persistenzkontext wird auch als application-managed persistence contextbezeichnet.

Java EE Web und EJB-Container unterstützen sowohl den container-managed als auch den application-managed persistence context. In Java-SE-Umgebungen ist laut Spezifikation lediglich der application-managed persistence context zu unterstützen, da dort in der Regel kein entsprechender Container für die Verwaltung des EntiyManagers vorhanden ist.

Der Lebenszyklus eines Persistenzkontexts kann entweder an die Lebensdauer einer Transaktion gebunden sein oder die Dauer einer einzelnen Transaktion weit überschrei-ten. Im ersten Fall spricht man von transaction-scoped persistence context, im zweiten Fall von einem extended persistence-context. Der Lebenszyklus des Persistenzkontexts wird beim Erstellen des EntityManagers festgelegt.

Für einen container-managed persistence context ist der Default für den Lebenszyklus entsprechend dem einer Transaktion. Beim application-managed persistence context wird auch der Lebenszyklus durch die Anwendung gesteuert, wodurch in diesem Fall ein erweiterter Persistenzkontext zum Einsatz kommt, der nicht an eine Transaktion gebun-den ist.

Der erweiterte Persistenzkontext beginnt mit dem Erzeugen des EntityManagers und endet bei dessen Schließen. Während dieser Lebensspanne können zahlreiche Transak-tionen durchlaufen und ebenso viele EntityManager-Aufrufe ohne Transaktion durch-geführt werden.

Der EntityManager des extended persistence context behält die Referenzen der verwalte-ten Entities, nachdem die Transaktion beendet wurde. Veränderungen an den Entities oder Aufrufe von merge(), persist(), remove() und refresh() werden erst bei einem Com-mit der nächsten Transaktion mit in die Datenbank übernommen. Es ist zu beachten, dass beim Starten einer neuen Transaktion die Entites nicht erneut aus der Datenbank geladen werden. Dies kann natürlich zu den Problemen führen, die bereits in Kapitel 6.2aufgeführt und besprochen wurden.

Die Navigation von einem verwalteten Objekt im Persistenzkontext zu einem anderen Objekt resultiert darin, dass das angesteuerte Objekt nun ebenfalls im Persistenzkontext verwaltet wird. Dies ist unabhängig davon, ob eine Transaktion offen ist oder nicht.

6.3.2 Erzeugen eines EntityManagers

Im Folgenden soll das Erstellen eines EntityManagers für die verschiedenen Anwen-dungsfälle betrachtet werden. Dabei gilt prinzipiell, dass ein EntityManager mithilfe einer EntityManagerFactory erzeugt wird. Beim Einbinden von EntityManagern mittels Dependency Injection oder per JNDI lookup in Java-EE-Umgebungen wird die Erzeu-gung durch den Container und somit transparent für den Entwickler durchgeführt.

Page 142: Jpa Mit en

Entity Manager und Persistenzkontext

JPA mit Hibernate 141

Bei der Verwendung und Erzeugung von EntityManagern sollte immer daran gedacht werden, dass diese nicht threadsafe sind, sie sollten also nur in einem einzelnen Thread erzeugt und verwendet werden, da es sonst zu erheblichen Problemen mit Nebenläufig-keit und inkonsistenten Daten kommen kann.

Java-EE-Umgebungen

In einer Java EE Umgebung mit container managed persistence context wird ein Entity Manager nicht direkt über die EntityManagerFactory erstellt, sondern per Dependency Injection oder JDNI lookup eingebunden.

Der Persistenzkontext wird erstellt, wenn ein Aufruf an den injizierten EntityManager bei aktiver JTA-Transaktion erfolgt und noch kein aktiver Persistenzkontext vorhanden ist.

In Listing 6.6 wird gezeigt, wie mit der Annotation @PersistenceContext ein EntityMana-ger mittels Dependency Injection erzeugt wird.

Das Attribut type der Annotation @PersistenceContext gibt die Art des verwendeten Per-sistenzkontexts an. Dieser ist per Default in Java-EE-Umgebungen PersistenceContenxt-Type.TRANSACTION.

Ein container managed persistence context vom Typ PersistenceContextType.EXTENDEDkann im Java-EE-Umfeld nur in einer stateful session Bean initiiert werden. Dabei wird der Persistenzkontext im Initialisierungsprozess der zugehörigen stateful session Beanerzeugt und beim Zerstören der stateful session Bean beendet.

Wird in einer stateful session Bean eine weitere stateful session Bean erzeugt, wird der Persistenzkontext an die erzeugte stateful session Bean vererbt und erst dann geschlos-sen, wenn alle stateful session Beans, die mit dem Persistenzkontext durch die Verer-bung verbunden waren, zerstört wurden.

In Listing 6.7 wird die Erzeugung eines EntityManagers mittels JNDI lookup gezeigt.

@PersistenceContextEntityManager em;

@PersistenceContext(type=PersistenceContextType.EXTENDED)EntityManager emExtended;

Listing 6.6: Erzeugen eines Entitymanagers mittels Dependency Injection

@Stateless@PersistenceContext(name="OrderEM")public class MySessionBean implements MyInterface { @Resource SessionContext ctx; public void doSomething() { EntityManager em = (EntityManager)ctx.lookup("OrderEM");

Listing 6.7: Erzeugen eines Entity Managers mittels JNDI lookup

Page 143: Jpa Mit en

6 – Transaktionen, EntityManager und Persistenzkontext

142

Mit @Resource wird der Kontext für den JNDI lookup in die stateless session Bean gela-den. Die Methode lookup() gibt anschließend den entsprechenden EntityManager zurück.

Ein application managed EntityManager kann ausschließlich über eine EntityManager-Factory erstellt werden. Das folgende Listing zeigt die Erzeugung einer EntityManager-Factory mit der Annotation @PersistenceUnit.

Die Methode createEntityManager() der EntityManagerFactory gibt einen EntityManger zurück und erstellt einen Persistenzkontext, dessen Lebenszyklus durch die Anwen-dung gesteuert wird und der mit dem Aufruf von close() des EntityManagers geschlos-sen werden kann. Der Methode createEntityManager kann bei Bedarf eine Map mit Pro-perties übergeben werden, die für den EntityManager gelten sollen. Die Methode close()schließt die EntityManagerFactory und gibt alle noch zugeordneten Ressourcen wieder frei. Die Methode isOpen() verrät, wie der Name schon andeuten, ob die EntityMana-gerFactory noch verwendbar ist und somit noch EntityManager erzeugen kann.

Wird die EntityManagerFactory geschlossen, während noch eine Transaktion aktiv ist, so bleibt der Persistenzkontext bis zur Beendigung der Transaktion ebenfalls aktiv.

Java-SE-Umgebungen

In Java-SE-Umgebungen kann nur ein application managed persistence context zum Ein-satz kommen, da kein Java-EE-Container zur Verfügung steht, der die Verwaltung über-nehmen könnte. Die Erzeugung von EntityManagern erfolgt für den application managed persistence context analog zu den Java-EE-Umgebungen über eine EntityManagerFac-tory. Allerdings wird die EntityManagerFactory nicht über Dependency Injection in die entsprechende Klasse eingebunden, sondern über die statische Methode createEntityMa-nagerFactory() der Klasse javax.persistence.Persistence geladen. Das Beispiel in Listing 6.8 zeigt die Verwendung der Methode.

... }}

@PersistenceUnitEntityManagerFactory emf;

Neu in JPA 2.0

Da das Interface der EntityManagerFactory in JPA 2.0 um zahlreiche Methoden im Zusammenhang mit Caching, dem Metamodell, Properties und dem Persis-tenceUnitUtil erweitert wurde, hat man nun die Möglichkeit, jeden Entity-Manager mithilfe eines Getters nach seiner EntityManagerFactory zu fragen.

Listing 6.7: Erzeugen eines Entity Managers mittels JNDI lookup (Forts.)

i

Page 144: Jpa Mit en

Patterns für EntityManager und Hibernate Session

JPA mit Hibernate 143

6.4 Patterns für EntityManager und Hibernate Session

In den foglenden Kapiteln werden zwei Patterns mit zugehörigen Beispielen für die Ver-wendung des EntityManagers vorgestellt. Es ist wichtig, die Einsatzgebiete und die Unterschiede der beiden vorgestellten Patterns zu verstehen, da durch falsche Verwen-dung die Performance stark abnehmen kann, wie die Anti-Patterns am Ende des Abschnitts beweisen.

In den vorgestellten Beispielen wird ein EntityManager verwendet, jedoch sind die Pat-terns für die Hibernate Session analog verwendbar.

Die beiden vorgestellten Patterns werden dabei bereits für den EntityManager durch den PersistenceContextType TRANSACTION und EXTENDED für den container managed persistence context umgesetzt.

6.4.1 „EntityManger/Session per Request“-Pattern

Das einfachste und am häufigsten verwendete Pattern ist auch als Session/EntityMana-ger per Request Pattern bekannt. Dabei wird jeweils pro Nutzeranfrage ein EntityMana-ger bzw. eine Session erzeugt. Dieses Verhalten wird bereits im Container Managed Per-sistence Context mit PersistenceContextType.TRANSACTION umgesetzt. Im Folgenden (Listing 6.9) soll gezeigt werden, wie das Pattern in Java-SE-Umgebungen mit einem application managed persistence context angewendet werden kann.

// Mit dem übergebenen Parameter wird der Name der PersistenceUnit übergeben, für // die die EntityManagerFactory erzeugt werden soll.EntityManagerFactory emf = javax.persistence.Persistence.createEntityManagerFactory("booksonlinePU");EntityManager em = emf.createEntityManager();

Listing 6.8: Erzeugen einer EntityManagerFactory in einer Java-SE-Umgebung

// User Request trifft ein// EntityManager wird erzeugt EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager()EntityTransaction tx;try { tx = em.getTransaction(); tx.begin();// Transaktion wird gestartet

//Datenbankoperationen ausführen ... tx.commit(); // Transaktion wird geschlossen

Listing 6.9: Das EntityManager/Session-per-Request-Pattern

Page 145: Jpa Mit en

6 – Transaktionen, EntityManager und Persistenzkontext

144

In diesem Fall wird, nachdem eine Anfrage eines Users eingetroffen ist, ein EntityMana-ger erzeugt. Im Anschluss wird eine Transaktion gestartet. Nun werden alle Datenbankoperationen, die zur Bearbeitung des Requests notwendig sind, ausgeführt. All diese Operationen laufen im Kontext der zu Beginn gestarteten Transaktion. Zum Schluss wird die Transaktion geschlossen. Beim Auftreten von Exceptions wird die Transaktion mit einem Rollback beendet. Danach wird noch der EntityManager geschlossen.

Bei diesem Szenario besteht eine 1-zu-1-Beziehung zwischen EntityManager und Trans-aktion, d. h., die „Lebensdauer“ eines EntityManagers entspricht genau der zugehörigen Transaktion. Der EntityManager wird unmittelbar nach Abschluss der Transaktion mit close() beendet.

Diese Vorgehensweise ist für die meisten Multi-User-Anwendungen die beste Strategie, um eine hohe Performance und gute Skalierbarkeit zu gewährleisten. Man ist gut beraten, mit dieser einfach umzusetzenden Vorgehensweise zu beginnen und das nachfolgende alterna-tive Pattern erst einzusetzen, wenn man an die Grenzen dieses Pattern stößt.

Mit den in Kapitel 5 vorgestellten Detached Entities können einzelne Entities immer wie-der einem aktuellen EntityManager zugeordnet werden, sodass sich auch eine längere Interaktionskette zwischen Benutzer und Anwendung mit dem EntityManager/Session-per-Request-Pattern umsetzen lässt. So könnte beispielsweise ein erster EntityManager eine Entity laden, die dann an den Benutzer zurückgegeben wird. Nachdem die Entity vom Benutzer verändert wurde, wird sie in einem zweiten Request an die Anwendung zum Speichern übertragen. Die Entity wird nun an einen zweiten, neu erzeugten Entity-Manager gebunden. Dabei ist es sinnvoll die in Abschnitt 6.2 vorgestellte automatische Versionsüberprüfung mit optimistischem Locking zum Einsatz kommen zu lassen. Die so an den neuen EntityManager gebundene Entity wird dann im Kontext des zweiten EntityManagers bzw. der zugehörigen Transaktion gespeichert. Entscheidend ist dabei, dass erst in der letzten Transaktion Änderungen gespeichert werden und alle vorangegangenen Transaktionen nur lesend auf die Daten zugreifen. Sonst wäre die Iso-lation der „Benutzertransaktion“ nicht gewährleistet, denn durch die Aufteilung der „Benutzertransaktion“ in mehrere Datenbanktransaktionen würden Änderungen für parallele Transaktionen bereits sichtbar, bevor die gesamte Transaktion aus Sicht des Benutzers abgeschlossen ist. Diese Vorgehensweise wird dann auch als „EntityManager / Session per Request with detached objects“ bezeichnet.

} catch (Exception e) { if (tx!=null) tx.rollback(); //evtl. Abbruch der Transaktion throw e;} finally { em.close(); // EntityManager wird geschlossen}

Listing 6.9: Das EntityManager/Session-per-Request-Pattern

Page 146: Jpa Mit en

Patterns für EntityManager und Hibernate Session

JPA mit Hibernate 145

6.4.2 „EntityManager/Session per Conversation“-Pattern

Das im vorangegangenen Abschnitt vorgestellte Pattern stößt an seine Grenzen, wenn für eine längere Interaktionskette zwischen einem Remote-Client und der Anwendung viele Entities über längere Zeit benötigt werden. Wird das Pattern in diesem Fall verwen-det, so müssten die Daten jedes Mal neu aus der Datenbank geladen werden. Auch die Verwendung von Detached Entities ist in so einem Fall nicht wirklich zu empfehlen, da diese jedes Mal übertragen oder immer wieder bei dem gerade aktivem EntityManager registriert werden müssten.

Beim EntityManager/Session-per-Conversation-Pattern wird der EntityManager nach Abschluss einer Transaktion nicht geschlossen. Bei Verwendung eines container managed persistence context mit PersistenceContextType.EXTENDED wird dieses Verhalten bereits umgesetzt. Zu beachten ist dabei allerdings, dass der Persistenzkontext in einer stateful session Bean verwendet werden muss.

Bei der Verwendung eines application managed persistence context, wie bspw. in Java-SE-Umgebungen, muss das Pattern per Hand umgesetzt werden, wie Listing 6.10 zeigt.

In obigem Beispiel wird zu Beginn ein neuer EntityManager erzeugt. Nach Beginn der ersten Transaktion wird ein User geladen. Die Transaktion wird gleich im Anschluss wie-der beendet. Der EntityManager wird, im Gegensatz zum EntiyManager/Session-per-Request-Pattern, nicht geschlossen, sondern bleibt erhalten. Damit bleiben auch alle Entities, die über den EntityManager geladen wurden, erhalten.

// EntityManager wird erzeugt EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager()EntityTransaction tx;tx = em.getTransaction(); tx.begin();// Transaktion wird gestartet//Laden einer EntityUser user = (User) em.find(User.class, new Long(42));tx.commit(); // 1. Transaktion wird beendet

//Die Entity wird vom Benutzer verändert:user.setFirstname("Max");user.setLastname("Muster");

Transaction tx2 = = em.getTransaction(); tx2.beginn(); // Start der zweiten Transaktionem.lock(user, LockModeType.READ); // wurde die Entity von einer // parallelen Transaktion verändert?em.flush(); // Änderungen in Datenbank übernehmentx2.commit(); // 2. Transaktion wird beendet.em.close();

Listing 6.10: Das EntityManager/Session-per-Conversation-Pattern

Page 147: Jpa Mit en

6 – Transaktionen, EntityManager und Persistenzkontext

146

Nun wird die Entity bspw. durch Benutzereingaben verändert. Diese Benutzereingaben können dabei ruhig länger dauern, denn es ist ja keine Transaktion offen, die evtl. andere Transaktionen aufhält. In obigem Beispiel werden der Vor- und Nachname des Users ver-ändert. Die Entity ist dabei die gesamte Zeit an den EntityManager gebunden, d. h., im Gegensatz zur Verwendung von Detached Entities ist es nicht notwendig, die Entities wieder an einen EntityManager zu binden.

Um die Änderungen in die Datenbank zu übernehmen und den Use Case damit abzu-schließen, wird eine zweite Transaktion geöffnet.

Im Anschluss werden durch einen expliziten Aufruf von flush() die Änderungen in die Datenbank geschrieben. Mit dem Commit der zweiten Transaktion werden die Ände-rungen dauerhaft in die Datenbank übernommen. Nun wird die Session geschlossen und der Anwendungsfall ist damit beendet.

Wie im obigen Beispiel demonstriert, wird beim EntityManager/Session-per-Conversation-Pattern der EntityManager nicht geschlossen. Er bleibt bis zum Ende der gesamten „Unter-haltung“, also eines Anwendungsfalls, erhalten. Die Beziehung zwischen Transaktion und EntityManager ist nicht mehr eins zu eins, sondern n Transaktionen zu einem EntityMana-ger. Durch wiederholtes Aufrufen von getTransaction() des EntityManagers wird eine neue Transaktion erzeugt und mit begin() auf der zurückgegebenen Transaction Instanz gestartet. Beim Commit einer Transaktion wird die Zuordnung dieser Transaktion zum EntityMana-ger beendet, sodass jederzeit eine neue Transaktion gestartet werden kann.

Die automatische Versionsüberprüfung von JPA bei Verwendung eines @Version-Attribu-tes verhindert das Auftreten von LostUpdates. Die Versionsüberprüfung wird, wie bereits in Kapitel 6.2 erläutert, automatisch bei der Durchführung der entsprechenden Datenbank-Updates durchgeführt. Damit hat man die Wahl, auf Überschneidungen durch parallele Transaktionen folgendermaßen zu reagieren:

� Last Commit Wins: Die Transaktion, die als Letztes beendet wird, überschreibt die Änderungen der parallelen Transaktion. Für dieses Verhalten ist keine Versions-überprüfung, bzw. optimistisches Locking, erforderlich. Es ist sozusagen das Default-Verhalten von JPA, wenn kein Versionsattribut für Entities angegeben wird.

� First Commit Wins: Die Änderungen der ersten Transaktion bewirken eine Erhö-hung der Version einer bestimmten Entity innerhalb der Datenbank. Dies wird beim Commit der nachfolgenden parallelen Transaktionen erkannt, und die Änderungen der nachfolgenden Transaktionen werden somit nicht durchgeführt. Stattdessen wird der Benutzer auf die parallelen Änderungen aufmerksam gemacht, und der ent-sprechende Anwendungsfall wird neu gestartet. Für dieses Verhalten kann die auto-matische Versionsüberprüfung von JPA verwendet werden.

Das EntityManager/Session-per-Conversation-Pattern ist die richtige Wahl, wenn große Datenmengen über einen längeren Zeitraum zur Verfügung stehen müssen und es wäh-rend dieser Zeit zu mehr oder weniger beliebig langen „Denkpausen“ des Benutzers kommen kann. Allerdings erhöht sich durch die Verwendung des EntityManagers als Cache für Entities die Wahrscheinlichkeit, veraltete Daten im Speicher zu halten. Beson-ders bei sich häufig ändernden Datensätzen ist Vorsicht bei der Verwendung dieses Pat-terns geboten, denn das schönste Caching hilft nichts, wenn am Ende der Transaktion alles verworfen werden muss, weil mit veralteten Daten gearbeitet wurde.

Page 148: Jpa Mit en

Patterns für EntityManager und Hibernate Session

JPA mit Hibernate 147

6.4.3 „EntityManager/Session per Operation“- und „EntityManager/Session per Application“-Antipattern

In diesem Abschnitt werden nun zum Abschluss, sozusagen als Kontrastprogramm, die zwei gängigsten Antipatterns vorgestellt. Antipatterns sind Vorgehensweisen, die so gut wie nie angewandt werden sollten, da sie mehr Probleme schaffen als sie lösen.

Ein erstes Antipattern bzgl. der Verwendung von Transaktionen und EntityManager ist das so genannte EntityManager/Session-per-Operation-Pattern. Dabei wird für jede ein-zelne Datenbankoperation ein eigener EntityManager samt zugehöriger Transaktion benutzt. Dies entspricht vom Prinzip her der Verwendung des Auto-Commit-Modus einer Datenbank. Jede Operation wird als eigenständige Transaktion ausgeführt. Hän-gen aufeinanderfolgende Datenbankoperationen voneinander ab, ist ein gemeinsamer Rollback nicht mehr möglich.

Es sollte bereits an dieser Stelle klar sein, dass auf diese Weise keine vernünftige Umset-zung einer komplexeren Anwendung mit zahlreichen und umfangreichen UseCases möglich ist. Daher sollte, wie bereits erwähnt, der Auto-Commit-Modus der Datenbank stets deaktiviert und ein EntityManager immer für die Bearbeitung eines kompletten UseCases, oder zumindest einer zusammengehörigen Untermenge davon, verwendet werden.

Das zweite Antipattern wird auch EntityManager/Session per Application genannt. Dabei wird ein EntityManager für mehrere Anwendunsfälle, oder noch wesentlich schlimmer, für die gesamte Anwendung verwendet. Don´t try this at home!

Ein EntityManager ist nicht Thread-sicher, d. h., alle Zugriffe durch parallele User-Requests müssten auf diesen einen EntityManager synchronisiert werden. Skalierbarkeit kann dadurch in keinem Fall erreicht werden.

Tritt eine Exception auf, muss der beteiligte EntityManager stets geschlossen werden, da der Inhalt des EntityManagers nicht mehr mit dem Inhalt der Datenbank übereinstimmt und keine Synchronisation zwischen den beiden mehr stattfindet. Wird nun ein Entity-Manager für die gesamte Anwendung verwendet, müsste die gesamte Anwendung geschlossen werden, sobald ein Anwendungsfall eine Datenbank-Exception auslöst.

Ein EntityManager ist immer auch ein Cache für alle Entities, die über ihn geladen wer-den. Wird ein EntityManager also für eine gesamte Anwendung verwendet, landen dort über kurz oder lang große Datenmengen. Es besteht zwar die Möglichkeit, über clear() den Cache des EntityManagers zu leeren, eine praktikable Lösung ist dies aber trotzdem nicht, da nach jedem Anwendungsfall alle verwendeten Entities manuell aus dem Enti-tyManager entfernt werden müssten. Außerdem würde ein solcher globaler Cache die Wahrscheinlichkeit stark erhöhen, mit veralteten Daten zu arbeiten, was zu häufigen Abbrüchen von Anwendungsfällen führen würde.

Kurz und knapp zusammengefasst: Man sollte nie einen EntityManager pro Anwen-dung verwenden, in der parallele Benutzeranfragen behandelt werden sollen.

Page 149: Jpa Mit en

6 – Transaktionen, EntityManager und Persistenzkontext

148

6.5 ZusammenfassungIn diesem Kapitel wurden die Grundlagen von Datenbanktransaktionen sowie deren Anwendung in JPA und Hibernate vorgestellt.

JPA und Hibernate ermöglichen, auf einfache und transparente Weise die Transaktionen der Datenbank zur Sicherung von parallelen Benutzeroperationen zu benutzen. Hiber-nate als JPA PersistenceProvider führt dabei keine neuen Mechanismen, wie z. B. das Sperren von Entities im Arbeitsspeicher, ein, sondern setzt konsequent auf die Funktio-nalität der bereits in JDBC vorhandenen Transaktionsmechanismen. Dabei wurden auch die Unterschiede im Locking zwischen Hibernate und JPA und die Neuerung in Bezug auf das Locking in JPA 2.0 vorgestellt.

Die Möglichkeiten der Erzeugung eines EntityManagers in Java-SE- und Java-EE-Umge-bungen wurden ebenso beleuchtet wie deren Zusammenhang zum Persistenzkontext.

Die in den letzten Abschnitten beschriebenen Patterns beschreiben Lösungsansätze, die für die meisten Anwendungsfälle zu einer stabilen und einfach umzusetzenden Archi-tektur führen. Vom Einsatz der in Kapitel 6.4.3 beschriebenen Antipatterns kann für so gut wie alle Anwendungsfälle nur abgeraten werden.

Page 150: Jpa Mit en

JPA mit Hibernate 149

7 Datenbankabfragen mit JPA und Hibernate

In diesem Kapitel werden die vielfältigen Möglichkeiten vorgestellt, um Entities auf ein-fache Art und Weise aus der Datenbank zu laden. Zuerst werden die zentralen Interfaces für die Abfragen mit Unterschieden und Gemeinsamkeiten zwischen Hibernate und JPA betrachtet. Im Anschluss findet die objektorientierte Abfragesprache JPQL (Java Persis-tence Query Language) Beachtung, sie ist sehr ähnliche zu HQL (Hibernate Query Lan-guage) auf die aus diesem Grund nur am Rande eingegangen wird. Hibernate bietet zusätzlich die Möglichkeit, Entities mittels eines Criteria API zu laden. Am Ende des Kapitels wird dann noch das neue Criteria API von JPA 2.0 vorgestellt.

7.1 Das Query InterfaceDas Query Interface ist die zentrale Schnittstelle zum Ausführen von Abfragen. Sie kommt immer dann zum Einsatz, wenn die Primärschlüssel von Entities nicht bekannt sind. Mithilfe bestimmter Suchkriterien, z.B. den Werten einzelner Attribute einer Entity, können diese Entities aber in der Datenbank gefunden werden. Eine Query-Instanz wird mithilfe des aktuellen EntityManagers erzeugt:

Der übergebene String-Parameter der createQuery()-Methode muss dabei eine gültige JPQL-Abfrage sein. Die korrekte Syntax wird in den folgenden Kapiteln erläutert.

Hibernate bietet mit org.hibernate.Query ein analoges Interface für das Ausführen von Abfragen. Die Erzeugung einer Instanz wird folgendermaßen mithilfe einer Session vor-genommen:

Die Session wird hier über die Methode getDelegate() des EntityManagers referenziert. Als Übergabeparameter von Typ String der createQuery()-Methode wird ein gültiger HQL String erwartet.

7.1.1 Ausführung der Abfragen

In Tabelle 7.1 werden die möglichen Methoden für die Ausführung der Abfragen des JPA- und Hibernate-Interfaces gegenüber gestellt:

EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager(); Query jQuery = em.createQuery("Select b from Book b");

Session s = (Session)em.getDelegate(); org.hibernate.Query hQuery = s.createQuery("from Publisher");

Page 151: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

150

Die durch die Abfrage gefundenen Entities werden durch den entsprechenden Entity-Manager verwaltet, sodass Änderungen an Attributen automatisch bei einem Commit oder Flush in die Datenbank übernommen werden.

Das Query Interface kann sowohl in JPA als auch in Hibernate skalare Ergebnisse liefern. In diesem Fall werden als Ergebnis einfach Instanzen der entsprechenden Wrapper-Klas-sen (Integer, Long, etc.) zurückgegeben. Liefert eine Abfrage mehr als eine Instanz je Zeile, wird ein Object Array geliefert.

JPA Hibernate Beschreibung

getResultList() query.list() Das Ergebnis wird als java.util.List zurückgegeben und somit komplett in den Speicher geladen.

getSingleResult() uniqueResult() Falls sichergestellt ist, dass die Abfrage nur eine einzige Entity zurückliefert, kann mit dieser Methode der Umweg über die Liste vermieden werden. Im Fall, dass die Abfrage dennoch mehr Entities liefert, wird eine Exception geworfen.

- iterate() Ermöglicht die Iteration über die Ergebnismenge. Im Unter-schied zu getResultList() und list() werden die Ergebnisse nicht komplett im Speicher gehalten, sondern es wird für jedes Element in der Ergebnismenge eine separate Abfrage durchgeführt. Die erste Abfrage liefert nur die Iden-tifier der Entities. Dieses Verhalten führt dazu, dass die Ver-wendung von iterate() normalerweise langsamer ist als die von list(). Sollten sich aber viele Entities der Ergeb-nismenge bereits im Speicher, also in der Session oder dem Second Level Cache, befinden, ist iterate() schneller.

- scroll() Unterstützt der verwendete JDBC-Treiber scrollbare Result-Sets, kann diese Methode benutzt werden. Ihre Verwendung ähnelt jener von iterate() mit dem Unterschied, dass man sich in der Ergebnismenge vor- und rückwärts bewegen kann.

Tabelle 7.1: Methoden für das Ausführen von Abfragen

Neu in JPA 2.0

Bei der Ausführung der Query ist der Rückgabewert nicht typisiert, sondern es wird eine untypisierte Liste oder lediglich Object zurückgegeben. Das Ergebnis muss demzufolge gecastet oder einer entsprechend typisierten Liste übergeben werden. Mit JPA 2.0 findet das TypedQuery-Interface Einzug in die Spezifikation. Mit diesem Interface ist es möglich, typisierte Anfragen abzu-setzen und somit mit getResultList() eine typisierte Liste oder mit getSingle-Result() die konkrete Entity zu erhalten:

TypedQuery<User> tQuery = em.createQuery(jpqlString, User.class) User user = tQuery.getSingleResult()

i

Page 152: Jpa Mit en

Das Query Interface

JPA mit Hibernate 151

Eine Abfrage von mehreren skalaren Werten könnte wie in Listing 7.1 implementiert wer-den:

Die Anzahl der gelieferten Ergebnisse kann mit setMaxResults(int maxResults) begrenzt werden. Durch die Methode setFirstResult(int firstResult) kann eine beliebige Anzahl von Zeilen übersprungen werden. Auf diese Weise können z.B. Ergebnisse in einzelne Abschnitte aufgeteilt und bspw. im Rahmen einer Webanwendung seitenweise ausge-geben werden:

Die Methoden setMaxResult() und setFirstResult() sind in gleicher Art sowohl in JPA als auch in Hibernate vorhanden.

Die meisten Methoden des Query Interface liefern die aktuelle Query-Instanz als Rück-gabewert. Wie man in obigem Beispiel sieht, können die meisten Methoden deshalb gut kaskadiert werden.

jQuery = em.createQuery("select p.description, count(b) from Publisher p inner join p.books b group by p.description having count(b) = 2");lObjArys = jQuery.getResultList();for (Object[] lAry : lObjArys) { System.out.println(lAry[0] + " hat " + lAry[1] + " Bücher.");}

Listing 7.1: Eine Abfrage mit skalaren Ergebnissen

Query jQuery = em.createQuery("Select b from Book b") .setMaxResults(10).setFirstResult(5);List<Book> list = jQuery.getResultList();

Neu in JPA 2.0

In das Query Interface wurden mit JPA 2.0 neue Methoden aufgenommen. So kann man mit getMaxResult() und getFirstResult() die gesetzten Werte auch auslesen. Ebenso dient die Methode getHints() der Überprüfung gesetzter Einstellungen für den Persistence Provider, der die Abfrage ausführen soll. Mit setHint() (bereits seit JPA 1.0 enthalten) kann man nun auch den neu stan-dardisierten Parameter javax.persistence.query.timeout setzen, der die maxi-male Zeit (in Millisekunden) definiert, die auf die Ausführung der Query gewartet wird. Ist die Zeit abgelaufen, wird eine QueryTimeoutException geworfen.

Das Locken von Entites (Kapitel 6) kann durch Verwenden von setLockMode()auch mit dem Auführen der Query vorgenommen werden. Dabei liefert die Methode getLockMode(), wie nicht schwer zu erraten, den aktuell gesetzten Lock-Modus zurück.

i

Page 153: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

152

7.1.2 Parameter Binding

Das Query Interface bietet sowohl in JPA als auch in Hibernate Methoden, mit denen Parameter in eine konkrete Abfrage eingefügt werden können. Die Kennzeichnung der Parameter erfolgt dabei entweder mittels Namen oder mittels Index.

Die Verwendung von benannten Parametern in JPA ist in Listing 7.2 zu sehen.

Die Verwendung der indexbasierten Parameter wird in Listing 7.3 gezeigt:

Beide Arten von Parametern können in beliebiger Reihenfolge und mehrfach in der Abfrage angegeben werden. Die Verwendung von benannten Parametern hat den Vor-teil, dass sie aussagekräftiger sind.

Die Syntax für die Angabe von benannten Parametern ist in Hibernate analog. Jedoch unterscheidet sich die Angabe bei den indexbasierten Parametern, wie in Listing 7.4 zu sehen ist.

jQuery = em.createQuery("Select b from Book b where b.title = :title and b.ISBN = :isbn");jQuery.setParameter("title", "Buch 1");jQuery.setParameter("isbn", "1111");

Listing 7.2: Verwendung von benannten Parametern

jQuery = em.createQuery("Select b from Book b where b.title = ?2 and b.ISBN = ?1");jQuery.setParameter(2, "Buch 1");jQuery.setParameter(1, "1111");

Listing 7.3: Verwendung von indexbasierten Parametern

Neu in JPA 2.0

Die Typisierung der Abfragen, die mit dem TypedQuery-Interface begonnen hat, wird auch bei den Parametern umgesetzt. In JPA 2.0 wurde aus diesem Grund das typisierte Parameter-Interface eingeführt, mit dem Name und Posi-tion eines Parameters gespeichert wird. Die Parameter Instanzen einer Query können mit getParameters() oder getParameter() mit Angabe von Name oder Position geholt werden. Das Setzen der Parameter erfolgt über setParameter() wo nun auch die konkrete Parameterinstanz mit übergeben werden kann. Daneben wurde mit isBound() eine Methode aufgenommen, der lediglich eine Parameterinstanz übergeben wird und damit prüft, ob ein Wert für den ent-sprechenden Parameter gesetzt ist.

i

Page 154: Jpa Mit en

Das Query Interface

JPA mit Hibernate 153

Im Gegensatz zu JPA wird lediglich das Fragezeichen '?' ohne anschließende konkrete Indexzahl als Platzhalter in der Abfrage verwendet. Dadurch können die Parameter nicht mehrfach verwendet werden und die Reihenfolge ist festgelegt. Der Index der Parameter beginnt bei Hibernate immer bei Null!

7.1.3 Definition von benannten Abfragen in den Metadaten

Es ist möglich, benannte Abfragen in den Mapping-Metadaten mittels Annotationen zu definieren. Auf diese Abfragen kann dann mittels des angegebenen Namens zugegriffen werden. Daraus ergibt sich der Vorteil, dass man die Abfragen mehrfach verwenden kann und dass die Wartbarkeit erhöht wird, da es möglich ist, alle benötigten Abfragen an einer zentralen Stelle vorzuhalten. Zur Definition einer Abfrage wird @NamedQuery ver-wendet:

Die Abfrage aus Listing 7.5 wird wie folgt genutzt:

In Hibernate kann die Annotation @org.hibernate.annotations.NamedQuery für benannte Abfragen verwendet werden. Erzeugung, Parameterübegabe und Ausführung sind ana-log zu JPA.

hQuery = s.createQuery("Select b from Book b where b.title = ? and b.ISBN = ?");hQuery.setParameter(0, "Buch 1");hQuery.setParameter(1, "1111");

Listing 7.4: Verwendung der indexbasierten Parameter in Hibernate

@Entity@NamedQuery(name="booksonline.bo.Book.bookByIsbn", query="Select b from Book b where b.ISBN=:isbn")public class Book {...

Listing 7.5: Definition einer benamten Query

jQuery = em.createNamedQuery("booksonline.bo.Book.bookByIsbn"); jQuery.setParameter("isbn", "1111");System.out.println(((Book)jQuery.getSingleResult()).getTitle());

Page 155: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

154

7.2 Die Java Persistence Query LanguageDie Java Persistence Query Language, kurz JPQL, ist eine sehr mächtige Abfragespra-che, die in ihrem prinzipiellen Aufbau an SQL erinnert. Im Gegensatz zu SQL ist JPQL komplett objektorientiert und unterstützt beispielsweise Abfragen von Objekten über deren Vererbungsbeziehung.

Die Abfragen im vorherigen Abschnitt wurden bereits in JPQL definiert. In diesem Abschnitt wird die Syntax der JPQL vorgestellt und anhand von Beispielen die Ver-wendung der JPQL demonstriert.

7.2.1 Allgemeines

Generell ist die Syntax der JPQL nicht case sensitive. Lediglich Java-Klassen und deren Properties müssen bzgl. Groß- und Kleinschreibung korrekt angegeben werden.

Die Syntax der JPQL orientiert sich stark an SQL. An einigen Stellen unterscheidet sich allerdings die Bedeutung der entsprechenden Ausdrücke. So bedeutet beispielsweise der Ausdruck from User in SQL, dass Daten aus der Tabelle USER abgefragt werden, wohingegen derselbe Ausdruck in JPQL besagt, dass Entities vom Typ User oder einem Subtyp abgefragt werden sollen.

7.2.2 Übersicht der Beispieldaten

In Tabelle 7.2 sind die Daten aufgelistet, die als Grundlage für die Beispiele in den folgen-den Abschnitten dienen. Es stehen zehn Book, drei Publisher und zwei BookCategory Enti-ties zur Verfügung. Dabei besteht zwischen Book und Publisher eine n-zu-1-Beziehung und zwischen Book und BookCategory eine m-zu-n-Beziehung.

Buch (Titel, ISBN, Autor, Pages) Verlag (Name) Kategorie (Name)

Book 1, 1111, Author ONE, 123 Publisher ONE Category ONE, Category TWO

Book 2, 2222, Author TWO, 132 Publisher ONE Category ONE, Category TWO

Book 3, 3333, Author THREE, 111 Publisher ONE Category ONE

Book 4, 4444, Author FOUR, 231 Publisher TWO Category ONE, Category TWO

Book 5, 5555, Author FIFE, 102 Publisher TWO Category ONE

Book 6, 6666, Author SIX, 95 Publisher TWO Category TWO

Book 7, 7777, Author SEVEN, 337 Publisher TWO Category TWO

Book 8, 8888, Author EIGHT, 173 Publisher TWO -

Book 9, 9999, Author NINE, 60 Publisher THREE -

Book 9, 0000, Author TEN, 230 Publisher THREE -

Tabelle 7.2: Beispieldaten

Page 156: Jpa Mit en

Die Java Persistence Query Language

JPA mit Hibernate 155

7.2.3 Grundaufbau der Abfragen

Analog zu SQL werden auch in JPQL für jede Abfrage mindestens ein select- und ein from-Block benötigt. Mit from wird die Menge der möglichen Rückgabewerte, der Werte-bereich, für die Abfrage angegeben. Des Weiteren werden in from Identifikationsvariab-len definiert, die in den anderen Blöcken bspw. als Referenz für Vergleiche verwendet werden können. Der konkrete Typ, den die Abfrage aus dem in from defienierten Bereich zurückliefert, wird im select angegeben.

Eine einfache Abfrage, die alle persistenten Entites vom Typ Book (inklusive aller Sub-klassen) aus der Datenbank zurückliefert, kann wie folgt geschrieben werden:

Die Angabe des vollqualifizierten Entity-Namens ist hierbei optional und muss nur angegeben werden, wenn andernfalls keine Eindeutigkeit gegeben ist. Für die Definition des Alias der Identifikationsvariablen ist das as optional und kann dementsprechend weggelassen werden. Somit kann man obige Abfrage auch wesentlich kürzer definieren:

Im angegebenen Beispiel ist die Entity Book der Wertebereich der Abfrage, für den die Identifikationsvariable b definiert wird. An dieser Stelle wurde das optionale Schlüssel-wort as weggelassen. Jedoch kann man zur besseren Übersicht auch from Book as bschreiben. Im select wird b referenziert, wodurch der Rückgabetyp der Abfrage auf Bookfestgelegt wird. Jedoch kann man auch im select entlang den Referenzen der Entity navi-gieren. So gibt das folgende Beispiel die Beschreibung des Publishers von Book zurück:

Jedes JPQL Statement wird vom Persistenzprovider in ein SQL Statement umgewandelt. Ist in der Konfigurationsdatei persitence.xml die Einstellung

gesetzt, werden alle verwendeten SQL Statements im Log ausgegeben. Vor allem für das Debugging sind diese Ausgaben sehr hilfreich, produzieren aber auch entsprechend große Logdateien, wodurch ein Aktivieren dieser Option in einer Produktivumgebung nicht sinnvoll ist.

So findet man für das obige Beispiel folgendes SQL Statement in den Logausgaben:

select b from booksonline.bo.Book as b

select b from Book b

select b.publisher.description from Book b

<property name="hibernate.show_sql" value="true"/>

select publisher1_.description as col_0_0_ from Book book0_, Publisher publisher1_ where book0_.publisher_myId=publisher1_.myId

Page 157: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

156

Werden mehrere Entity-Klassen als Wertebreich angegeben, resultiert dies in einem kar-tesischen Produkt aus den beteiligten Klassen, wie im folgenden Beispiel gezeigt wird:

Als Rückgabe erhält man für dieses Statement eine Liste von Objekt-Arrays, wobei das erste Element im Array eine Entity vom Typ Publisher und das zweite Element eine Entity vom Typ Book ist.

In HQL ist es möglich, das select bei einer Abfrage wegzulassen. So kann man beispiels-weise für das letzte Statement in HQL auch lediglich

schreiben. Verwendet man Hibernate als Persistenzprovider, braucht man ebenso in JPQL das select in den Abfragen nicht anzugeben. Dies ist aber nicht zu empfehlen, da dadurch die Kompatibilität der JPQL-Abfragen bei der Verwendung alternativer Persis-tenzprovider nicht mehr gegeben ist.

7.2.4 Einschränkung der Ergebnismenge mit „where“

Die Ergebnismenge kann durch Verwendung von where eingeschränkt werden. Durch Angabe von Bedingungen wird das Ergebnis auf die Entities eingeschränkt, die die Bedingungen erfüllen.

Eine einfache Abfrage könnte wie folgt aussehen:

Mit dieser Abfrage werden alle Entities vom Typ Book zurück geliefert, die den Titel „Book 9“ haben. Auf unsere Testdaten ausgeführt, werden die beiden folgenden Daten-sätze zurückgegeben:

Für das Einschränken der Ergebnismenge stehen vielfältige Ausdrücke und Funktionen zur Verfügung, die in den folgenden Abschnitten näher erläutert werden.

Logische Operatoren

Für die Verknüpfung von Ausdrücken stehen die bekannten logischen Operatoren and, or und not zur Verfügung. Dabei können die einzelnen Ausdrücke mittels runder Klam-mern ' (' und ') ' gruppiert werden, um Blöcke zu bilden und eine bessere Übersicht in komplexen Abfragen zu erhalten.

select p,b from Publisher p, Book b

from Publisher p, Book b

Select b from Book b where b.title = 'Book 9'

[Book] title = "Book 9" author = "Author TEN" ISBN = "0000"[Book] title = "Book 9" author = "Author NINE" ISBN = "9999"

Page 158: Jpa Mit en

Die Java Persistence Query Language

JPA mit Hibernate 157

Literale und Indentifikationsvariablen

In JPQL werden alle üblichen SQL-Literale für die Darstellung von Strings, Zahlen und Booleans unterstützt. Dabei sind jedoch einige Punkte zu beachten:

� Strings werden in einzelnen Anführungszeichen eingeschlossen: 'Hibernate'

� Bei der Angabe von Zahlen muss unter Umständen, bspw. bei Long oder Float, der konkrete Typ als Suffix angegeben werden: 7000000000L, 1234.5678F

� Vordefinierte reservierte Literale, wie bspw. TRUE oder FALSE, können sowohl groß als auch klein geschrieben werden: TRUE == true

Vergleichsoperatoren

Bei den Vergleichsoperatoren kann man die gebräuchlichen =, >, <, >=, <= verwenden. Für ungleich benutzt man den Operator <>. Der aus SQL bekannte like-Operator kann eben-falls genutzt werden, wobei die nachfolgenden Beispiele den Gebrauch verdeutlichen sollen:

resultiert in

und

resultiert in

Im ersten Beispiel werden alle Bücher ausgegeben, deren Autoren auf „EN“ enden. Im zweiten Fall jedoch wird das Buch vom Autor „Author NINE“ nicht ausgegeben, da _nur für ein einzelnes Zeichen steht.

Die Sonderzeichen _ und % können mittels ESCAPE wie folgt in einer Abfrage verwendet werden:

Neu in JPA 2.0

Ab der Version 2.0 der JPA-Spezifikation sind auch Datums- und Zeitliterale in JDBC-Escape-Syntax zur Verwendung in JPQL-Abfragen erlaubt:

SELECT o FROM Order o WHERE o.date < {d '2008-12-31'}

Select b from Book b where b.author like '%EN'

[Book] title = "Book 7" author = "Author SEVEN" ISBN = "7777"[Book] title = "Book 9" author = "Author TEN" ISBN = "0000"

Select b from Book b where b.author like 'Author _NE

[Book] title = "Book 1" author = "Author ONE" ISBN = "1111"

Select b from Book b where b.author like '%t%NE' ESCAPE 't'

i

Page 159: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

158

Die Ergebnismenge ist leer, da nach Büchern mit Autoren gesucht wird, die auf “%EN” enden. Dabei ist die Definition von t als Escape-Zeichen nicht üblich, aber wie zu sehen, möglich.

Für Vergleiche auf leere Attribute stehen die aus SQL bekannten Operatoren is null undis not null zur Verfügung.

Mit JPA besteht des Weiteren die Möglichkeit, ein Attribut mit einer Vielzahl von Werten eines Subqueries (Kapitel 7.2.11) zu vergleichen. Dafür stehen die Operatoren all und anyzur Verwendung bereit. Bei all muss für alle Werte der Subquery der Vergleich wahr sein, wie Listing 7.6 zeigt.

Als Ergebnis werden alle Entites vom Typ Book zurückgegeben, deren Seitenzahl (pages) größer ist als die Seitenzahl aller Bücher, die dem „Publisher THREE“ zugeordnet sind.

Im Gegensatz zu all reicht es beim Operator any aus, dass der Vergleich mit mindestens einem Wert aus dem Subquery übereinstimmt. In Listing 7.7 werden alle Bücher gesucht, deren Seitenzahl größer ist als die Seitenzahl eines der Bücher von „Publisher ONE“.

Als Ergebnis werden die Entites aus Listing 7.8 geliefert.

Select b from Book b where b.publisher.description <> 'Publisher THREE' and b.pages > ALL (Select b2.pages from Book b2 where b2.publisher.description = 'Publisher THREE')

Listing 7.6: Beispiel für die Verwendung des all-Operators

[Book] title = "Book 9" author = "Author TEN" ISBN = "0000"" pages = "230"[Book] title = "Book 7" author = "Author SEVEN" ISBN = "7777"" pages = "337"[Book] title = "Book 4" author = "Author FOUR" ISBN = "4444"" pages = "231"

Select b from Book b where b.publisher.description <> 'Publisher ONE' and b.pages > ANY (Select b2.pages from Book b2 where b2.publisher.description = 'Publisher ONE')

Listing 7.7: Beispiel für die Verwendung des any-Operators

[Book] title = "Book 9" author = "Author TEN" ISBN = "0000"" pages = "230"[Book] title = "Book 7" author = "Author SEVEN" ISBN = "7777"" pages = "337"[Book] title = "Book 4" author = "Author FOUR" ISBN = "4444"" pages = "231"[Book] title = "Book 8" author = "Author EIGHT" ISBN = "8888"" pages = "173"

Listing 7.8: Ergebnis der Abfrage aus Listing 7.7

Page 160: Jpa Mit en

Die Java Persistence Query Language

JPA mit Hibernate 159

Mengenoperatoren

Für den Vergleich von und mit Mengen gibt es in JPQL eine breite Palette von Möglich-keiten. Als Operatoren stehen in, between, is empty, und member of zur Verfügung, die jeweils durch Verwendung von not negiert werden können.

Der in-Operator vergleicht die Entity oder ein entsprechendes Attribut mit einer kom-maseparierten Liste möglicher Werte:

Die Abfrage liefert die Bücher

zurück. Die Liste der Werte muss in Klammern eingeschlossen werden und kann neben der konkreten Werten auch ein entsprechendes Subquery enthalten.

Der between-Operator dient der Bequemlichkeit und besseren Übersichtlichkeit, da mit ihm der Ausdruck y <= x AND x <= z einfach als x BETWEEN y AND z geschrieben werden kann. Mit der Beispielabfrage sollen alle Bücher gefunden werden, die eine ISBN zwi-schen 3 und 5 haben. Hier ist zu beachten, dass das Attribut ISBN der Entity Book als String definiert ist.

Das Ergebnis der Abfrage sollte nicht überraschen:

Mit dem Operator is empty kann man überprüfen, ob eine Collection leer ist oder nicht (is not empty). Die folgende Abfrage soll alle Entities vom Typ Book zurückliefern, die kei-ner BookCategory zugeordnet sind.

Select b from Book b where b.ISBN in ('2222','4444')

[Book] title = "Book 2" author = "Author TWO" ISBN = "2222"[Book] title = "Book 4" author = "Author FOUR" ISBN = "4444"

Neu in JPA 2.0

Statt einer Liste von einzelnen Werten kann mit JPA 2.0 auch eine Collection als Parameter der in-Operation übergeben werden:

Select b from Book b where b.ISBN in :listOfISBNs

Select b from Book b where b.ISBN between '3' and '5'

[Book] title = "Book 3" author = "Author THREE" ISBN = "3333"[Book] title = "Book 4" author = "Author FOUR" ISBN = "4444"

Select b from Book b where b.bookCategories is empty

i

Page 161: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

160

Das Ergebnis beinhaltet die folgenden Entites:

Statt der Benutzung von is empty kann man auch die Größe der Collection mit dem size-Operator wie folgt überprüfen. Das Ergebnis ist das gleiche.

Im Gegensatz zu is empty, der lediglich prüft, ob Elemente vorhanden sind, kann man mit dem Operator member of abfragen, ob eine bestimmte Entity in einer Collection ent-halten ist. Die folgende Abfrage soll alle Entities vom Typ Book liefern, die der BookCate-gory „Category ONE“ zugeordnet sind.

Die zurückgelieferten Entites sind in Listing 7.9 zu sehen.

Mathematische, String- und Datumsfunktionen

Für die Ausdrücke, mit denen die Attribute der Entities verglichen werden sollen, stehen eine Vielzahl an Operatoren und Funktionen zur Verfügung, um die Zahlen und die Lite-rale zu manipulieren.

Neben den Grundrechenarten mit +,-,* und / stehen als mathematische Funktionen noch der absolute Betrag abs(number), die Quadratwurzel sqrt(number) und der Modulus mod(number,number) zur Verfügung.

Für die Arbeit mit Strings stehen in JPQL die Funktionen concat(), lower(), upper(), length(), substring(), trim() und locate() bereit.

[Book] title = "Book 9" author = "Author TEN" ISBN = "0000"[Book] title = "Book 8" author = "Author EIGHT" ISBN = "8888"[Book] title = "Book 9" author = "Author NINE" ISBN = "9999"

Select b from Book b where size(b.bookCategories) = 0ODERSelect b from Book b where b.bookCategories.size = 0

Select b from Book b, BookCategory bc where bc member of b.bookCategories and bc.description = 'Category ONE'

[Book] title = "Book 1" author = "Author ONE" ISBN = "1111"[Book] title = "Book 2" author = "Author TWO" ISBN = "2222"[Book] title = "Book 3" author = "Author THREE" ISBN = "3333"[Book] title = "Book 4" author = "Author FOUR" ISBN = "4444"[Book] title = "Book 5" author = "Author FIFE" ISBN = "5555"

Listing 7.9: Ergebnis der Beispielabfrage mit member of

Page 162: Jpa Mit en

Die Java Persistence Query Language

JPA mit Hibernate 161

Mit concat(A, B) können zwei Strings miteinander zu AB verbunden werden. Die String-Funktionen lower() und upper() wandeln alle Zeichen des übergebenen Strings in Klein- bzw. Großbuchstaben um, sodass aus lower(aA) => aa und aus upper(bB) => BB wird. Die Länge eines Strings ermittelt man einfach mit der Funktion length(). Um einen Teil-String aus einem gegebenen String zu extrahieren, bietet sich die Funktion substring(String, Start, Länge) an:

Es ist zu beachten, dass bei der Funktion substring in JPQL, anders als in Java, das erste Zeichen die Position 1 und nicht 0 hat, deshalb wird auch 'hor SE' als Ergebnis zurückge-liefert.

Mit der Funktion trim(Position Zeichen FROM String) können Zeichen am Anfang (Posi-tion = LEADING) und am Ende (Position = TRAILING) eines Strings entfernt werden. Dabei werden als Default jeweils die Zeichen am Anfang und am Ende (Position = BOTH) ent-fernt. Als zu entfernendes Zeichen wird, sofern nichts anderes angegeben, das Leerzei-chen angenommen. Die folgende Abfrage liefert beispielsweise als Ergebnis 'Book ' (mit Leerzeichen am Ende).

Es ist allerdings zu beachten, dass nicht alle Datenbanken andere Zeichen als das Leer-zeichen für die Funktion trim() akzeptieren. Aus diesem Grund sollte man, um maxi-male Portabilität zu erreichen, auf andere Zeichen in trim() verzichten.

Die Funktion locate(Zeichenfolge, String, Startindex) versucht, eine bestimmte Zei-chenfolge in dem übergebenen String zu finden und gibt bei Erfolg die Position im String und andernfalls 0 zurück. Wie bei der Funktion substring() hat das erste Zeichen im String die Position 1. Mit dem dritten Parameter kann man optional die Startposition der Suche angeben, wobei als Default immer ab Anfang des Strings gesucht wird. In der nun folgenden Abfrage wird die Zeichenkette 'hor SE’ gesucht und an Position '4' gefunden.

Da bei Vergleichen rund um das Datum vor allem der aktuelle Zeitpunkt eine wesent-liche Rolle spielt, bietet JPQL drei Funktionen für dessen Abfrage. Mit current_date wird das aktuelle Datum (2010-02-22), mit current_time die aktuelle Zeit (21:42:56) und mit current_timestamp ein Zeitstempel (2010-02-22 21:42:56.531) zurückgegeben.

// Author von 'Book 7' ist 'Author SEVEN'Select substring(b.author,4,6) from Book b where b.title = 'Book 7'

Select trim(TRAILING '7' FROM b.title) from Book b where b.title = 'Book 7'

Select locate('hor SE', b.author) from Book b where b.title = 'Book 7'

Page 163: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

162

7.2.5 Sortierung mit „order by“

Die Ergebnisliste kann durch Verwendung von order by sortiert werden. Als Parameter ist jedes Attribut einer Entity möglich. Die Sortierung wird dabei nicht im Speicher durchgeführt, sondern durch die Datenbank.

Durch Angabe von asc (aufsteigend) oder desc (absteigend) kann die Sortierreihenfolge bestimmt werden. Per Default wird aufsteigend sortiert. Es können mehrere durch Komma getrennte Attribute angegeben werden. Die zuerst genannten Attribute haben bei der Sortierung den Vorrang.

Das obige Beispiel liefert eine nach Verlag sortierte Liste aller Entities Book. Bücher mit gleichem Verlag werden nach der Seitenzahl absteigend sortiert.

Neu in JPA 2.0

Mit dem case-Statement können ab JPA 2.0 auch Bedingungen in JPQL-Abfra-gen integriert werden und finden sowohl in Updates als auch Selects Verwen-dung:

Select b.title, b.author, CONCAT(CASE WHEN b.pages < 100 THEN 'small' WHEN b.pages > 300 THEN 'big' ELSE 'medium' END, ' Book') from Book b Update Book b set b.production_cost = CASE b.type WHEN 'paperback' THEN b.pages * 1.3 WHEN 'hardcover' THEN b.pages * 1.7 ELSE p.pages * 0.9 END

Im ersten Beispiel wird je nach Seitenzahl small, big oder medium mit dem String Book verbunden. Das zweite Beispiel verwendet das case-Statement, um die Produktionskosten eines Buchs abhängig vom Buchtyp zu berechnen.

Select b from Book b order by b.publisher, b.pages desc

i

Page 164: Jpa Mit en

Die Java Persistence Query Language

JPA mit Hibernate 163

7.2.6 Joins

Werden innerhalb einer Abfrage mehrere Entity-Klassen angegeben, resultiert dies in einem kartesischen Produkt, d.h., die Ergebnisliste enthält alle möglichen Kombinatio-nen der beteiligten Entities, was auch als full join bezeichnet wird.

So liefert folgende Abfrage viel mehr Ergebnisse als eventuell gewünscht:

Als Ergebnis erhält man jede mögliche Kombination zwischen Publisher- und Book-Instanzen. Da in den Beispieldaten drei Publisher und zehn Book Entities enthalten sind, werden konkret 30 Datensätze als Ergebnis übergeben.

Um eine korrekte Zuordnung zwischen Publisher und Book zu erreichen, ist ein so genannter inner join notwendig. Dieser führt zu einer 1:1-Zuordnung. Elemente, die keine Zuordnung in der jeweils anderen Tabelle haben, sind nicht im Ergebnis enthalten. Die Angabe des Schlüsselworts inner ist optional.

Wird nun die obige Abfrage mithilfe eines Inner Joins neu formuliert, werden die Book-Instanzen dem jeweiligen Verlag korrekt zugeordnet:

Als Ergebnis werden die zehn entsprechenden Paare Publisher – Book zurückgegeben.

Möchte man auch Elemente der linken Tabelle erhalten, denen kein Element aus der rech-ten Tabelle zugeordnet werden kann, muss man einen left outer join verwenden. Dabei

Neu in JPA 2.0

Die Sortierung mit order by kann mit JPA 2.0 auch auf die Attribute von Kom-ponenten (Embeddables) angewendet werden:

@Embeddable public class Address { private String city; ... } @Entity public class User { @Embedded private Address address; ... } Select b from Book b order by b.address.city

Select p,b from Publisher p, Book b

Select p,b from Publisher p inner join p.books b

i

Page 165: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

164

werden die fehlenden Elemente aus der rechten Tabelle durch null ersetzt. Bei der Kon-struktion der Abfrage ist die Verwendung von outer optional, wie folgendes Beispiel zeigt.

Die Abfrage liefert alle Bücher mit deren dazugehörigen Kategorien, dabei sind auch die Entities 'Book 8' und 'Book 9' im Ergebnis enthalten, für deren Kategorien null zurückge-geben wird.

Ein Spezialfall des left outer join ist der Fetch Join. Mit diesem Konstrukt ist es möglich, Referenzen und Assoziationen der abgefragten Entities mitzuladen und somit für die weitere Verwendung zur Verfügung zu stellen. In Listing 7.10 wird ein Beispiel mit und ohne Fetch Join gezeigt, um die Verwendung zu demonstrieren.

Im ersten Fall resultiert der Zugriff auf die Collection pub.getBooks() in einer weiteren Anfrage an die Datenbank, um die Book Entities nachzuladen. Im zweiten Fall werden durch den Fetch Join alle zugehörigen Book Entities mit der Anfrage nach dem Verlag aus der Datenbank gelesen. Somit kann man mithilfe von Fetch Joins Ressourcen beim Laden von Entities einsparen. In Kapitel 8 wird noch einmal näher auf Fetch Joins eingegangen.

Joins können in JPQL explizit oder implizit angegeben werden. Die implizite Form ähnelt dem Referenzieren von Attributen unter Java. Das Schlüsselwort join wird dabei nicht benötigt. So liefert z.B. die folgende Abfrage alle Entities vom Typ Publisher, die genau zwei Bücher besitzen:

Die Assoziation zu Book wird dabei nicht explizit, sondern implizit mit einem Inner Join aufgelöst. Implizite Joins sind immer Inner Joins.

Select b,c from Book b left join b.bookCategories c

jQuery = em.createQuery("Select p from Publisher p where p.description = 'Publisher THREE'");Publisher pub = (Publisher)jQuery.getSingleResult();System.out.println(pub + " | " + pub.getBooks().get(0) );

jQuery = em.createQuery("Select p from Publisher p left join fetch p.books where p.description = 'Publisher THREE'");lPub = jQuery.getResultList();for (int i=0;i<lPub.size();i++) { System.out.println(lPub.get(i) + " | " + lPub.get(i).getBooks().get(i) );}

Listing 7.10: Beispiel mit und ohne Fetch Join

Select p from Publisher p where books.size=2

Page 166: Jpa Mit en

Die Java Persistence Query Language

JPA mit Hibernate 165

7.2.7 Die „select“-Anweisung im Detail

Mithilfe des Schlüsselworts select kann in einer JPQL-Abfrage ausgewählt werden, wel-che Objekte und Attribute eine Abfrage als Ergebnis liefert. Dies wurde ja bereits in den vorangegangenen Abschnitten ausführlich genutzt und soll nun vertieft werden.

In den meisten bereits verwendeten Beispielen wurde jeweils im Select nur ein Entity-Typ referenziert. In diesen Fällen ist der Rückgabewert von getResultList() eine typi-sierte Liste auf die, wie in Listing 7.11 gezeigt, zugegriffen werden kann.

Bei der Angabe von verschiedenen Entity-Typen im select einer Abfrage erhält man als Ergebnis eine Liste von Object[], in dem die im select referenzierten Entites entspre-chend ihrer Reihenfolge enthalten sind. In diesen Fällen muss beim Casten der Ergeb-nisse etwas mehr Vorsicht herrschen (Listing 7.12) .

Natürlich ist es auch möglich, skalare Werte als Rückgabewert einer Select-Abfrage zu erhalten. (Listing 7.13)

jQuery = em.createQuery("Select p from Publisher p where books.size=2");List<Publisher> lPub = jQuery.getResultList();for (Publisher pub : lPub) { System.out.println(pub);}

Listing 7.11: Rückgabewerte einer Abfrage

jQuery = em.createQuery("Select p,b from Publisher p inner join p.books b where length(b.author) = 10");List<Object[]> lObjArys = jQuery.getResultList();for (Object[] lAry : lObjArys) { System.out.println(((Publisher) lAry[0]) + " : " + ((Book) lAry[1]));}

Listing 7.12: Rückgabe verschiedener Entity-Typen in einer Abfrage

jQuery = em.createQuery("Select book, p.description from Publisher p inner join p.books book where p.description like '%EI'"); List<Object[]> lObjArys = jQuery.getResultList(); for (Object[] lAry : lObjArys) { System.out.println(((Book) lAry[0]) + " : " + ((String) lAry[1])); }

Listing 7.13: Skalare Werte als Ergebnis einer Abfrage

Page 167: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

166

Als Alternative zum Object-Array kann das Ergebnis auch direkt an einen Konstruktor übergeben werden, um einen typsicheren Rückgabewert zu erhalten. Im Beispiel in Lis-ting 7.14 werden die zusammengehörigen Entities Book und Publisher jeweils in einer Instanz der Klasse BookAndPublisher zurückgegeben:

7.2.8 Aggregatfunktionen

Mit JPQL können nicht nur skalare Werte, wie beispielsweise Attribute von Entities, als Ergebnis einer Abfrage geliefert werden, sondern auch Aggregatfunktionen auf selbigen.

Als Funktionen stehen der Durchschnitt (avg()), die Summe (sum()), das Minimum und Maximum (min(), max()) sowie eine Zählfunktion (count()) zur Verfügung. Dabei können die Summen- und Durchschnittsfunktion lediglich auf Zahlenwerte angewandt werden. Die Funktionen min() und max() benötigen als Argumente sortierbare Typen, wie bei-spielsweise Zahlen, Strings oder Datumswerte. Mit count() können neben skalaren Wer-ten auch Referenzen auf Objekte und Identifiktionsvariablen übergeben werden.

Die Rückgabewerte der Funktion count() sind immer vom Typ Long und jene der Funk-tion avg() immer vom Typ Double. Bei den anderen Aggregatfunktionen ist der Rückga-bewert abhängig vom Typ der übergebenen Argumente. So geben die Minima- und Maxima-Funktionen jeweils den gleichen Typ zurück, der ihnen beim Aufruf übergeben wurde. Die Summenfunktion gibt für ganzzahlige Argumente Long, für Gleitkommazah-len Double und für BigInteger und BigDecimal jeweils den übergebenen Wert zurück.

class BookAndPublisher { Book b; Publisher p;

public BookAndPublisher(Book b, Publisher p) { this.b = b; this.p = p; }}

...

jQuery = em.createQuery("Select new booksonline.test.BookAndPublisher(book, p) from Publisher p inner join p.books book where p.description like '%EI'");List<BookAndPublisher> lBandP = jQuery.getResultList();for (BookAndPublisher bp : lBandP) { System.out.println(bp.b + " : " + bp.p);}

Listing 7.14: Übergabe des Abfrageergebnisses an einen Konstruktor

Page 168: Jpa Mit en

Die Java Persistence Query Language

JPA mit Hibernate 167

Wird in einer Abfrage ohne Ergebnisse die Zählfunktion verwendet, so ist der Rückgabe-wert von count() 0. Im gleichen Fall wäre der Rückgabewert für die anderen Aggregat-funktionen null.

Mit dem Schlüsselwort distinct können vor Anwendung einer Aggregatfunktion dop-pelte Werte aus dem Argumentenset entfernt werden. Nullwerte werden, unabhängig von distinct, niemals mit an eine Aggregatfunktion übergeben.

Die folgende Abfrage liefert die Anzahl aller Entites vom Typ Book, die im System vor-handen sind:

Für unsere Beispieldaten liefert die Abfrage 10. Wird allerdings distinct auf das Attribut title von Book angewandt, so ist das Ergebnis der folgenden Abfrage lediglich 9, da zwei von den zehn Büchern den gleichen Titel haben.

Im letzten Beispiel für die Aggregatsfunktionen soll die Durchschnittsseitenzahl aller Bücher mit folgender Abfrage bestimmt werden:

7.2.9 Die „group by“-Anweisung

Abfragen mit Aggregatfunktionen kann man mithilfe von group by gruppieren. Als Gruppierungskriterium sind dabei alle Entity-Attribute zulässig. Im folgenden Beispiel soll als Fortsetzung zum vorherigen Abschnitt die durchschnittliche Seitenzahl der Bücher je Verlag bestimmt werden.

Dabei kann man mit having die Gruppen weiter einschränken, wie die nächste Abfrage zeigt.

Als Ergebnis sind somit nur noch die Verlage mit einem ‚E’ am Ende des Namens enthal-ten.

Wie in SQL vorgeschrieben, muss auch mit JPQL jedes Element aus dem Select der Abfrage auch im Group-by erscheinen, natürlich mit Ausnahme der Aggregatsfunktionen.

Select count(*) from Book

Select count(distinct b.title) from Book b

Select avg(b.pages) from Book b

Select avg(b.pages), p.description from Book b, Publisher p where b.publisher = p group by p.description

Select avg(b.pages), p.description from Book b, Publisher p where b.publisher = p group by p.description having p.description like '%E'

Page 169: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

168

7.2.10 Polymorphe Abfragen

Im Gegensatz zu SQL, wo das Konzept der Vererbung völlig unbekannt ist, unterstützt JPQL polymorphe Abfragen. Als polymorphe Abfrage bezeichnet man Abfragen, die verschiedene Entity-Instanzen, die zu einer gemeinsamen Vererbungshierarchie gehö-ren, als Ergebnis liefern. Bei der Einführung der Sub-Entities Paperback und Audiobook zu Book würde man demzufolge mit der Abfrage

alle Entites vom Typ Book, Paperback und Audiobook erhalten.

Es können aber auch Klassen und Interfaces angegeben werden, womit die Abfragen Instanzen von allen Subklassen oder Klassen liefern, die das Interface implementieren oder von der angegeben Klasse erben.

Die obige Abfrage gibt somit alle Instanzen aller Entities aus der Datenbank als Ergebnis zurück.

Es ist zu beachten, dass bei der Ausführung dieser polymorphen Abfragen mehrere SQL-Select-Statements benötigt werden, da die Daten ja meist aus verschiedenen Tabellen geladen werden müssen. Mittels order by spezifizierte Sortierungen können sich demzu-folge immer nur auf jene Teile beziehen, die aus der gleichen Tabelle geladen werden.

Select b from Book b

Neu in JPA 2.0

Möchte man mit der Abfrage nach den Entites vom Typ Book nicht auch alle abgeleiteten Entities erhalten, kann man ab JPA 2.0 mit der Funktion TYPE das Ergebnis entsprechend einschränken:

SELECT b FROM Book b WHERE TYPE(b) IN (Paperback, Audiobook) SELECT b,TYPE(b) FROM Book b WHERE TYPE(b) <> Paperback

Mit der ersten Abfrage werden alle Entities vom Typ Paperback und Audiobookzurückgegeben, jedoch keine Entites vom Typ Book. Die zweite Abfrage liefert alle Entities, die nicht vom Typ Paperback sind. Zusätzlich wird der Typ der Entities als Rückgabewert mitgeliefert.

Select o from java.lang.Object o

i

Page 170: Jpa Mit en

Native SQL

JPA mit Hibernate 169

7.2.11 Subqueries

Wenn die verwendete Datenbank Subqueries unterstützt, können diese auch in JPQL verwendet werden. JPQL erlaubt die Verwendung von Subqueries innerhalb von select-und where-Anweisungen.

Die obige Abfrage liefert alle Bücher, die mehr Seiten haben als der Durchschnitt aller Bücher in der Datenbank.

Liefert eine Subquery mehr als einen einzelnen Wert, so können diese folgendermaßen verwendet werden:

7.2.12 Massen-Update und -Delete

Die JPQL erlaubt Updates und Deletes im Stil eines SQL-Update bzw. -Delete. Damit ist es möglich, mehrere Einträge einer Tabelle mit einem Datenbankzugriff zu aktualisieren bzw. zu löschen.

Ein Massen-Update mit der JPQL des JPA kann beispielsweise so aussehen:

Ein Massen-Delete kann beispielsweise folgendermaßen aussehen:

Zu beachten ist, dass update und delete der JPQL im Gegensatz zum direkten Daten-bankzugriff mit SQL auch den Zustand der Entities entsprechend ändert.

7.3 Native SQLTrotz der umfangreichen Möglichkeiten zur Formulierung von Abfragen mittels JPQL bleiben bestimmte datenbankspezifische Funktionalitäten verborgen. JPA bietet daher die Möglichkeit, Abfragen direkt in SQL zu definieren, und ermöglicht somit auch die Nutzung dieser speziellen Funktionalitäten.

Generell sollten nur die Abfragen in SQL formuliert werden, die nicht durch eine der ande-ren Möglichkeiten ausgedrückt werden können. Denn jede in SQL formulierte Abfrage erhöht die Kopplung zwischen der Anwendung und der verwendeten Datenbank. Beim Wechsel der Datenbank müssen alle SQL-Abfragen unter Umständen an die neue Syntax angepasst werden. Durch Vermeidung von in SQL formulierten Abfragen lässt sich dieser Aufwand minimieren.

Select b from Book b where b.pages > (Select avg(b.pages) from Book b)

Select b from Book b where not (b.title,b.ISBN) in (SELECT b.title,b.ISBN from Book b inner join b.bookCategories bc where bc.description like '%ONE')

update Book b set b.author = 'newname' where b.author = 'oldname'

delete Book b where b.author = 'oldname'

Page 171: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

170

Ein weiterer sinnvoller Anwendungsfall von SQL-Abfragen mit JPA ist die Migration einer bestehenden, auf JDBC und SQL basierenden Persistenzlösung. In diesem Fall wird die Migration schrittweise ermöglicht, in der zu Beginn praktisch alle SQL-Abfragen direkt übernommen werden und erst nach und nach z.B. durch entsprechende JPQL-Abfragen ersetzt werden.

Eine SQL-Abfrage wird mithilfe der Methode createNativeQuery des EntityMangers erzeugt (Listing 7.15):

Der Methode createNativeQuery können drei verschiedene Parametersätze übergeben werden:

� createNativeQuery(String query): Die SQL-Query wird als String übergeben. Dieser Methodenaufruf ist vor allem für Update- und Delete-Statements zu empfehlen, die kein Resultset liefern.

� createNativeQuery(String query, Class entityclass): Wie im ersten Fall wird hier die SQL-Query als String übergeben. Zusätzlich wird mit dem zweiten Parameter der Entitytyp angegeben, der im Resultset zu erwarten ist.

� createNativeQuery(String query, String resultsetmapping): Auch in dem dritten und letzten möglichen Parametersatz wird die SQL-Query als String übergeben. Als zwei-ter Parameter wird der Name des SqlResultSetMappings angegeben. Dies ist nötig, falls mit dem Query mehr als eine Entity-Klasse oder eine Entity-Klasse und skalare Werte zurückgegeben werden. Die Definition eines SqlResultSetMappings erfolgt mittels Annotationen und wird im Folgenden noch ausführlich behandelt.

Die bereits vorgestellte Abfrage in SQL soll nicht nur Entities vom Typ Book, sondern auch noch die Entities vom Typ Publisher zurückgeben. Hierfür ist es nötig, die Annota-tion @SqlResultSetMapping in einer Entity-Klasse, bspw. Book, wie folgt zu definieren:

Die Annotation @EntityResult gibt den Entity-Typ für das Resultset an. Das Attribut name in @SqlResultSetMapping wird in der Methode createNativeQuery als Parameter ver-wendet (Listing 7.16).

Query jQuery = em.createNativeQuery("select b.* from Book b, Publisher p where p.myid=b.publisher_myid and p.description='Publisher ONE'", Book.class); List<Book> lBook = jQuery.getResultList(); for (Book b : lBook) { System.out.println(b); }

Listing 7.15: Verwenden von native SQL

@SqlResultSetMapping(name="twoEntites", entities={ @EntityResult(entityClass=booksonline.bo.Book.class), @EntityResult(entityClass=booksonline.bo.Publisher.class)})

Page 172: Jpa Mit en

Native SQL

JPA mit Hibernate 171

Bei Namenskonflikten zwischen den einzelnen Spalten und den in den Entites definier-ten Attributen ist es nötig, die Annotation @EntityResult durch ein Mapping mittels der Annotation @FieldResult zu ergänzen und in dem SQL-Query entsprechende Aliase zu verwenden (Listing 7.17).

Soll das native SQL-Statement sowohl Entites als auch skalare Werte zurückgeben, ist in der Annotation @SqlResultSetMapping das Attribut columns, wie in Listing 7.18 gezeigt, zu verwenden.

Query jQuery = em.createNativeQuery("select * from Book b, Publisher p where p.myid=b.publisher_myid and p.description='Publisher ONE'", "twoEntites"); List <Object[]> lObjAry = jQuery.getResultList(); for (Object[] objAry : lObjAry) { System.out.println(objAry[0] + " | " + objAry[1]); }

Listing 7.16: Native SQL mit Resultset Mapping

@EntityResult(entityClass=booksonline.bo.Book.class, fields={ @FieldResult(name="id",column="book_id") @FieldResult(name="title",column="book_title"), @FieldResult(name="ISBN",column="book_ISBN") ... })...

select b.id as book_id,b.title as book_title,b.ISBN as book_ISBN, ...

Listing 7.17: Verwenden von @FieldResult

@SqlResultSetMapping(name = "twoEntites", entities = {@EntityResult(...)}, columns = @ColumnResult(name = "publisher_description")))...

select b.*,p.description as publisher_description ...

Listing 7.18: Entities und skalare Werte in @SqlResultSetMapping

Page 173: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

172

7.4 Criteria API in HibernateMit den Abfragesprachen JPQL und HQL sowie den Möglichkeiten, natives SQL auszu-führen, stehen bereits mächtige Werkzeuge zum Laden von Entites aus der Datenbank zur Verfügung. Hibernate bietet mit dem Criteria API eine komplett objektorientierte Alternative, die im folgenden Abschnitt beleuchtet werden soll. Hierbei gelten natürlich die gleichen Einschränkungen bezüglich der Portabilität wie bei allen bisher vorgestell-ten Hibernate-spezifischen Funktionalitäten.

Mithilfe einer Session, die mittels getDelegate() und einem Cast vom EntityManager zurückgeliefert wird, kann eine Instanz von Criteria erzeugt werden:

Der Methode createCriteria() wird eine Klasse als Parameter übergeben. Dieser Para-meter legt fest, welche Entities durch die Abfrage geliefert werden. Um das Ergebnis zu erhalten, muss mit list() die Abfrage ausgeführt werden.

7.4.1 Ausführung der Abfragen

Für das Ausführen der Abfragen stehen folgende Methoden zur Verfügung:

� list(): Liefert eine Liste, welche die Ergebnisse enthält

� uniqueResult(): Liefert ein einzelnes Objekt und spart damit den Umweg über eine Liste

� scroll(): Liefert ein ScrollableResults, mit dessen Hilfe man sich in der Ergebnis-menge der Abfrage vorwärts und rückwärts bewegen kann.

Neu in JPA 2.0

Mit JPA 2.0 wird nun ebenfalls ein Criteria API definiert, das mit dem zugehö-rigen Metamodell in Kapitel 7.6 näher vorgestellt wird.

Session session = (Session) em.getDelegate();Criteria criteria = session.createCriteria(Book.class);List books = criteria.list();

Criteria criteria = session.createCriteria(Book.class);ScrollableResults books = criteria.scroll();while (books.next()) { Book book = (Book) books.get(0); System.out.println(book);}

Listing 7.19: Ausführen einer Hibernate CriteriaQuery mit scroll()

i

Page 174: Jpa Mit en

Criteria API in Hibernate

JPA mit Hibernate 173

In Listing 7.19 wird die Methode scroll() zur Ausführung der Abfrage verwendet. Falls nötig, könnte während der Iteration über das Abfrageergebnis mittels previous() zum vorhergehenden Ergebnis zurückgeschaltet werden.

Wie auch in Query, kann über setMaxResults(int i) die maximale Anzahl von gelieferten Ergebnissen vor der Ausführung der Abfrage gesetzt werden. Über setFirstResult(int i) können Ergebnisse übersprungen und so eine seitenweise Abfrage realisiert werden.

In Listing 7.20 werden bei jedem Schleifendurchlauf maximal drei Entities vom Typ Bookabgefragt und dann mittels setFirstResult() zu den nächsten drei weitergeschaltet. Wie man an diesem Beispiel auch sieht, lässt sich eine Criteria-Instanz wiederverwenden. Es ist also nicht notwendig, nach jeder Abfrage eine neue Criteria-Instanz zu erzeugen.

7.4.2 Einschränkung der Ergebnismenge mit Restrictions

Die Formulierung von Bedingungen wird bei dem Criteria API nicht, wie in den Abfra-gesprachen JPQL oder HQL, mit Strings durchgeführt, sondern es werden stets Objekte verwendet. Eine konkrete Bedingung, wie z.B. title = 'Book 1' wird durch eine Instanz des Interface Criterion repräsentiert. Die Information title und 'Book 1' sind dann Attri-bute dieser Instanz. Instanzen von Criterion werden mithilfe der Klasse Restrictionserzeugt und zu komplexeren Bedingungen kombiniert.

Folgender Code führt eine Abfrage nach allen Büchern aus, deren Titel „Book 1“ ist:

Die Klasse Restrictions stellt statische Methoden zur Erzeugung und Kombination von Criterions zur Verfügung. Diese werden nun gruppiert nach Operator-Typ vorgestellt.

� Mit der Methode idEq(Object id) wird der Primärschlüssel der Entity mit dem Objekt id verglichen.

Criteria criteria = session.createCriteria(Book.class);criteria.setMaxResults(3);List books = criteria.list();for (int counter = 0; books.size() != 0; counter += 3) { System.out.println(books.size() + " books found"); criteria.setFirstResult(counter); books = criteria.list();}

Listing 7.20: Iteration über das Ergebnis mit setFirstResult() und setMaxResult()

Criteria criteria = session.createCriteria(Book.class);criteria.add(Restrictions.eq("title", "Book 1")); List<Book> books = criteria.list();

Page 175: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

174

� Für den Vergleich von Attributen mit einem vorgegebenen Objekt stehen die bekann-ten relationalen Operatoren bereit.

� Es können auch zwei Attribute einer Entity miteinander verglichen werden. Die ent-sprechenden Methoden lauten wir folgt:

Relationaler Operator Methodensignatur

=, Gleichheit eq(String param, Object obj)

!=, Ungleichheit ne(String param, Object obj)

like, Ähnlichkeit analog zu JPQL like(String param, Object obj)

ilike, wie like, jedoch keine Berücksichtigung der Groß/Kleinschreibung

ilike(String param, Object obj)

>, Größer als gt(String param, Object obj)

<, Kleiner als lt(String param, Object obj)

>=, Größer gleich ge(String param, Object obj)

<=, Kleiner gleich le(String param, Object obj)

Vergleich auf null isNull(String param)

Vergleich auf nicht null isNotNull(String param)

Gleichheit für alle in der Map angegeben Paare aus Attributnamen und Wert

allEq(Map map)

obj1 <= param AND param <= obj2, Vergleich eines Attributs im Wertebereich

between(String param, Object obj1, Object obj2)

Vergleich eines Attributs auf das Enthaltensein in einer Menge von Objekten

in(String param, Object[] objs)

in(String param, Collection objs)

Relationaler Operator für zwei Attribute Methodensignatur

Attribute gleich eqProperty(String param1, String param2)

Attribute ungleich neProperty(String param1, String param2)

Attribut 1 kleiner als Attribut 2 ltProperty(String param1, String param2)

Attribut 1 größer als Attribut 2 gtProperty(String param1, String param2)

Attribut 1 kleiner gleich Attribut 2 leProperty(String param1, String param2)

Attribut 1 größer gleich Attribut 2 geProperty(String param1, String param2)

Page 176: Jpa Mit en

Criteria API in Hibernate

JPA mit Hibernate 175

� Für Collection-Attribute können die folgenden Methoden genutzt werden:

� Die Verbindung der Criterions erfolgt über logische Operatoren, die mit den folgen-den Methoden abgebildet werden und jeweils wieder ein Criterion zurückgeben:

Es besteht außerdem die Möglichkeit, mithilfe der Methode sqlRestriction(String sql)direkt entsprechende Bedingungen in SQL-Syntax anzugeben:

Mit {alias} wird ein Platzhalter definiert, der innerhalb der SQL-Abfrage durch den Alias der entsprechenden Tabelle ersetzt wird.

Statt der Klasse Restrictions kann auch die Klasse org.hibernate.criterion.Property zur Erzeugung von Criterion-Instanzen verwendet werden:

Die Methoden von Property zur Erzeugung von Criterions entsprechen denen von Rest-rictions.

Mengenoperatoren Methodensignatur

Menge leer isEmpty(String param)

Menge nicht leer isNotEmpty(String param)

Anzahl Elemente gleich n sizeEq(String param, int n)

Anzahl Elemente ungleich n sizeNe(String param, int n)

Anzahl Elemente größer n sizeGt(String param, int n)

Anzahl Elemente kleiner n sizeLt(String param, int n)

Anzahl Elemente größer gleich n sizeGe(String param, int n)

Anzahl Elemente kleiner gleich n sizeLe(String param, int n)

Logischer Operator Methodensignatur

und and(Criterion c1, Criterion c2)

oder or(Criterion c1, Criterion c2)

nicht not(Criterion)

Criteria criteria = session.createCriteria(Book.class);criteria.add(Restrictions.sqlRestriction( "{alias}.title like '%ok%'"));

Criteria criteria = session.createCriteria(Book.class);criteria.add(Property.forName("name").like("%Max%"));

Page 177: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

176

In Listing 7.21 sind zur Demonstration einige der bereits in Kapitel 7.2 präsentierten JPQL-Abfragen mithilfe des Criteria API neu formuliert:

7.4.3 Sortierung mit org.hibernate.criterion.Order

Mithilfe der Methode addOrder(Order order) können beliebig viele Sortierkriterien zu einer Criteria hinzugefügt werden:

Im obigen Beispiel wird eine Liste von Büchern alphabetisch absteigend sortiert.

System.out.println("Bücher die zwei BookCategories haben");// jQuery = em.createQuery("Select b from Book b where b.bookCategories.size>=2");criteria = session.createCriteria(Book.class);criteria.add(Restrictions.sizeGe("bookCategories", 2));books = criteria.list();

System.out.println("Bücher die eine ISBN (String!) zwischen '3' und '5' haben");// jQuery = em.createQuery("Select b from Book b where b.ISBN between '3' and '5'");criteria = session.createCriteria(Book.class);criteria.add(Restrictions.between("ISBN", "3", "5"));books = criteria.list();

System.out.println("Bücher die entweder ISBN 2222 oder 4444 haben");// jQuery = em.createQuery("Select b from Book b where b.ISBN in ('2222','4444')");criteria = session.createCriteria(Book.class);criteria.add(Restrictions.in("ISBN", new String[]{"2222", "4444"}));books = criteria.list();

System.out.println("//Bücher deren Autor mit 'EN' endet");// jQuery = em.createQuery("Select b from Book b where b.author like '%EN'");criteria = session.createCriteria(Book.class);criteria.add(Restrictions.like("author", "%EN"));books = criteria.list();

Listing 7.21: Demonstration des Criteria API

Criteria criteria = session.createCriteria(Book.class); criteria.addOrder(Order.desc("title"));List<Book> books = criteria.list();

Page 178: Jpa Mit en

Criteria API in Hibernate

JPA mit Hibernate 177

Werden mehrere Sortierungskriterien angegeben, so entscheidet die Reihenfolge, in der die Order-Objekte hinzugefügt werden (Listing 7.22).

7.4.4 Assoziationen

Assoziationen zwischen Entities können durch die zwei folgenden Methoden von Crite-ria in Abfragen eingebunden werden:

� Criteria createCriteria(String associationPath): Es wird eine neue Criteria-Instanz erzeugt, die sich bereits auf die Elemente der durch associationPath benannten Asso-ziation bezieht. Mit dieser neuen Criteria-Instanz können weitere Bedingungen angegeben werden, die sich auf die Elemente der Assoziation beziehen.

� Criteria createAlias(String associationPath, String alias): Es wird keine neue Instanz von Criteria erzeugt, sondern die bestehende Instanz bezieht sich nun auch auf die Elemente der Assoziation. Die durch einen Join hinzugefügten Entities kön-nen über alias für weitere Bedingungen der Abfrage verwendet werden.

Die Abfrage in Listing 7.23 liefert alle Publisher, die ein Buch verlegen, dessen Autor auf die Zeichenkette „EN“ endet:

Die Abfrage in Listing 7.23 lässt sich alternativ auch wie in Listing 7.24 formulieren.

Werden die Abfragen nun z.B. mit list() ausgeführt, sind in der zurückgegebenen Liste alle Publisher-Entities enthalten, deren Assoziationen die Bedingungen erfüllen. Werden die Assoziationen nun navigiert, d.h. wird publisher.getBooks() ausgeführt, so werden dort alle Bücher zurückgegeben, d.h., es findet keine Filterung der Assoziationen durch die Criteria statt.

Criteria criteria = session.createCriteria(Book.class); criteria.addOrder(Order.desc("author"));criteria.addOrder(Order.asc("ISBN"));List<Book> books = criteria.list();

Listing 7.22: Sortieren mit addOrder()

Criteria criteria = session.createCriteria(Publisher.class);Criteria bookCriteria = criteria.createCriteria("books");bookCriteria.add(Restrictions.like("author","%EN"));List<Publisher> pubs = criteria.list();

Listing 7.23: Assoziationen mit dem Criteria API

Criteria criteria = session.createCriteria(Publisher.class);criteria.createAlias("ld.book", "bk");criteria.add(Restrictions.like("bk.author","%EN"));List<Publisher> pubs = criteria.list();

Listing 7.24: Assoziationen mit der Criteria-API-Alternative

Page 179: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

178

Um eine solche Filterung zu erreichen, kann mit criteria.setResultTransformer(Result-Transformer resultTransformer) ein so genannter ResultTransformer gesetzt werden. Für das obige Beispiel ist es z.B. sinnvoll, den ResultTransformer Criteria.ALIAS_TO_ENTITY_MAPzu verwenden.

7.4.5 Abfragen mit org.hibernate.criterion.Example

Die Klasse Example ermöglicht die Formulierung von Abfragen anhand von Beispiel-instanzen der gesuchten Entities (Listing 7.25).

In der Publisher-Instanz examplePublisher werden nur jene Attribute gesetzt, die für die Abfrage erforderlich sind. Mithilfe von Example.create(Object entity) wird eine Cri-terion-Instanz erzeugt, die die entsprechenden Bedingungen enthält. Dabei werden Versionsattribute, Primärschlüssel, Assoziationen und Attribute, die den Wert null besit-zen, ignoriert.

Die Verwendung von Example zur Erzeugung von Criterions kann auch beliebig mit der Verwendung von Restrictions oder Property kombiniert werden.

Damit bietet Hibernate eine sehr einfache Art, komplexe Suchmasken zu implementie-ren. So ist es z.B. möglich, innerhalb einer Suchmaske eine Entity zu erzeugen und mit den ausgewählten Attributen zu initialisieren. Diese Entity-Instanz kann dann direkt zur Erzeugung der Abfrage verwendet werden.

7.4.6 Die Klasse org.hibernate.criterion.DetachedCriteria

Die Klasse DetachedCriteria ermöglicht die Erzeugung einer Criteria außerhalb einer Hibernate-Session. Damit kann z.B. auf der Clientseite eine Abfrage formuliert werden, die dann im Server ausgeführt wird.

Zur Ausführung der Abfrage muss die DetachedCriteria-Instanz an eine Session gebun-den werden. Dies wird durch die Methode getExecutableCriteria(Session session) der Klasse DetachedCriteria ermöglicht.

Criteria criteria = session.createCriteria(Publisher.class);Publisher examplePublisher = new Publisher("Publisher THREE");criteria.add(Example.create(examplePublisher));List<Publisher> pubs = criteria.list();

Listing 7.25: Eine Criteria mit einer Beispiel-Entity erzeugen

DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Book.class);detachedCriteria.add(Restrictions.isEmpty("bookCategories"));EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager();Session session = (Session) em.getDelegate();Criteria criteria = detachedCriteria.getExecutableCriteria(session);

Listing 7.26: Verwendung einer DetachedCriteria

Page 180: Jpa Mit en

Hibernate-Filter

JPA mit Hibernate 179

Im Beispiel in Listing 7.26 wird zu Beginn eine Instanz einer DetachedCriteria erzeugt. Die Abfrage wird so formuliert, dass sie alle Book-Entities liefert, die keine BookCategoryhaben. Erst zu einem späteren Zeitpunkt wird eine Session geöffnet und eine ausführ-bare Criteria-Instanz durch getExecutableCriteria() erzeugt.

7.5 Hibernate-FilterIn Hibernate besteht die Möglichkeit, so genannte Filter im Mapping zu definieren. Diese Filter ermöglichen es die Ergebnismenge in ähnlicher Form wie eine durch die Annotation @org.hibernate.annotations.Where definierte where-Bedingung einzuschränken. Jedoch können einmal definierte Filter auf beliebig viele Klassen und Collections angewandt wer-den. Filter können mithilfe der Annotationen @org.hibernate.annotations.FilterDef und @org.hibernate.annotations.Filter angegeben werden.

Die Annotation FilterDef dient zur Definition eines allgemeinen Filters. Diesem werden ein Name sowie eine Liste von Parametern zugewiesen. Die Annotation Filter spezifi-ziert den konkreten Filter, der auf die Entity Book angewandt wird (Listing 7.27).

Dieser zugeordnete Filter kann nun jederzeit, wie in Listing 7.28 gezeigt, aktiviert werden:

List<Book> books = criteria.list();for (Book b : books) { System.out.println(b);}

@FilterDef(name="authorFilter", parameters={@ParamDef(name="author",type="string")})@Filter(name="authorFilter", condition=":author = author")public class Book implements Serializable {...

Listing 7.27: Definition von Filtern

EntityManager em = JpaUtil.getEntityManagerFactory().createEntityManager();Session session = (Session) em.getDelegate();Filter authorFilter = session.enableFilter("authorFilter");authorFilter.setParameter("author", "Author FIFE");Query jQuery = em.createQuery("select b from Book b");List<Book> books = jQuery.getResultList();for (Book b : books) { System.out.println(b);}

Listing 7.28: Verwendung von Filtern

Listing 7.26: Verwendung einer DetachedCriteria (Forts.)

Page 181: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

180

Die Methode enableFilter() der Hibernate-Session wird zur Aktivierung von Filtern ver-wendet, wobei der Name des Filters als Parameter übergeben wird. In der folgenden Zeile wird im Filter der Parameter author auf den Wert Author FIFE gesetzt. Nun ist der Filter einsatzbereit. Wird nun eine Abfrage, die sich auf Book-Entities bezieht, ausgeführt, werden nur jene Books als Ergebnis geliefert, die den Filterkriterien entsprechen. Die im Attribut condition angegebene Bedingung wird bei jeder Abfrage der where-Bedingung hinzugefügt und gilt somit zusätzlich zu eventuell vorhandenen Einschränkungen.

7.6 Criteria API und Metamodell in JPA 2.0Das mit JPA 2.0 eingeführte Criteria API ist sehr mächtig, flexibel und vielfältig, denn es wird der komplette Funktionsumfang der JPQL abgebildet. Zur Erzeugung von Criteria-Query stehen drei Varianten zur Auswahl:

� Statisches Metamodell

� Dynamisches Metamodell

� Referenzieren der Attribute über Namen

Bei der Verwendung des Metamodells hat man Informationen über den Aufbau der Enti-ties in speziellen Klassen zur Verfügung und kann damit die Attribute der Abfragen spe-zifizieren. Es steht aber ebenso der aus Hibernate bekannte Ansatz zur Verfügung, dass die Attribute der Entites mittels String-Parametern referenziert werden. Im Folgenden sollen nun die beiden Varianten des Metamodells, die Ausführung der CriteriaQuerys und die grundlegenden Methoden zum Aufbau der Abfragen, erläutert werden. Dabei wird in den Beispielen hauptsächlich das statische Metamodell verwendet. Am Ende des Abschnitts werden die Verwendung des dynamischen Metamodells und der String-Parameter vorgestellt, wobei lediglich die Parameter der Methoden, nicht aber deren Syntax verschieden voneinander sind.

7.6.1 Das statische Metamodell

Das statische Metamodell wird mithilfe eines Annotationsprozessors aus den Entity-Klassen generiert. Der Vorteil liegt darin, dass die Attribute bei der Generierung als kon-krete Attribute in der Metamodellklasse abgebildet sind. Dadurch kann man bei dem Aufbau einer Abfrage zum einen die Autovervollständigung der Entwicklungsumge-bung verwenden und zum anderen auch Tippfehler bei der Angabe der abzufragenden Attribute vermeiden.

Für die Generierung kann der „Hibernate Static Metamodel Generator“ verwendet wer-den. In den meisten Fällen wird dieser automatisch laufen, sofern sich das entsprechende jar-File im Klassenpfad befindet. Dies ist möglich, weil der Annotation-Prozessor als Service Provider registriert wird, da in der Datei javax.annotation.processing.Processorim Verzeichnis META-INF/services die Klasse org.hibernate.jpamodelgen.JPAMetaModel-EntityProcessor angegeben ist.

Page 182: Jpa Mit en

Criteria API und Metamodell in JPA 2.0

JPA mit Hibernate 181

In Listing 7.29 werden die generierten Klassen für die Entities Book, Publisher und Book-Category gezeigt.

@Entitypublic class Book implements Serializable { private static final long serialVersionUID = 1L; @Id private Long id; private String title; private String ISBN; private String author; private int pages; @ManyToOne private Publisher publisher; private Set<BookCategory> bookCategories = new HashSet<BookCategory>(); ...}

//Metamodell@StaticMetamodel(Book.class)public abstract class Book_ { public static volatile SingularAttribute<Book, Long> id; public static volatile SingularAttribute<Book, String> title; public static volatile SingularAttribute<Book, String> ISBN; public static volatile SingularAttribute<Book, String> author; public static volatile SingularAttribute<Book, Integer> pages; public static volatile SingularAttribute<Book, Publisher> publisher; public static volatile SetAttribute<Book, BookCategory> bookCategories;}

@Entitypublic class BookCategory implements Serializable { @Id private Long id; private String description; ...}

//Metamodell@StaticMetamodel(BookCategory.class)public abstract class BookCategory_ {

Listing 7.29: Generierte Metamodellklassen für die Entities Book, Publisher und BookCategory

Page 183: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

182

Die Metamodellklassen werden mit der Annotation @StaticMetamodel markiert und nach der zugehörigen Entity mit angehängtem Unterstrich benannt. Für die Attribute der Entities werden je nach Typ entsprechende Äquivalente definiert. So wird zum Beispiel für ein einfaches Attribut eines Basistyps oder eine einfache Referenz das Attribut public static volatile SingularAttribute<X, Z> z in der Metamodellklasse angelegt.

Die Klassen des statischen Metamodells können auch per Hand angelegt werden, wobei allerdings dieser Aufwand gegenüber den Vorteilen des statischen Metamodells kaum gerechtfertigt ist. Aus diesem Grund sollte immer die automatische Generierung des Metamodells verwendet werden.

7.6.2 Das dynamische Metamodell

Im Gegensatz zu dem generierten statischen Metamodell, kann man über den EntityMa-nager oder die EntityManagerFactory auf das dynamische Metamodell mittels der Methode getMetamodel() zugreifen. Das dadurch zugängliche Metamodell verfügt unter anderem über folgende Methoden:

� <X> EntityType<X> entity(Class<X> cls)

� <X> EmbeddableType<X> embeddable(Class<X> cls)

public static volatile SingularAttribute<BookCategory, Long> id; public static volatile SingularAttribute<BookCategory, String> description;}

@Entitypublic class Publisher implements Serializable { @Id private Long id; private String description; private List<Book> books; ...}

//Metamodell@StaticMetamodel(Publisher.class)public abstract class Publisher_ { public static volatile SingularAttribute<Publisher, Long> id; public static volatile SingularAttribute<Publisher, String> description; public static volatile ListAttribute<Publisher, Book> books;}

Listing 7.29: Generierte Metamodellklassen für die Entities Book, Publisher und BookCategory (Forts.)

Page 184: Jpa Mit en

Criteria API und Metamodell in JPA 2.0

JPA mit Hibernate 183

Dadurch werden die entsprechenden Metamodellklassen für Entities und Komponenten erzeugt. Auf ihnen können mit den entsprechenden Methoden die Attributbeschreibun-gen zur Verwendung in den CriteriaQuerys abgerufen werden. Folgendes Listing demonstriert das Vorgehen:

7.6.3 Das Criteria API

Listing 7.30 zeigt die grundsätzliche Verwendung des Criteria API an einem einfachen Beispiel. Es sollen alle Entities vom Typ Book abgerufen werden. Dies entspricht der JPQL-Abfrage Select b from Book b.

Eine CriteriaQuery wird mithilfe eines CriteriaBuilders erzeugt, der durch den EntityManageroder die EntityManagerFactory bereitgestellt wird. Beim Anlegen der CriteriaQuery wird stets der Typ des Rückgabewerts definiert. Mit der Methode from() wird der Startpunkt der Query bestimmt. Durch den Aufruf von select() auf der Query legt man den Rückgabewert fest. Mit dem EntityManger und dessen createQuery() Methode wird aus der CriteriaQuery eine TypedQuery, die durch getResultList() die gewünschten Entities zurückliefert.

Möchte man mehrere Typen von Rückgabewerten erhalten, muss man mit cb.create-TupleQuery() oder cb.createQuery<Tuple.class> eine entsprechende Query erzeugen. Über das Tuple-Interface kann man typsicher auf die einzelnen Typen der Abfrage zugreifen.

Joins und FetchJoins

In Listing 7.31 wird mit der Methode join die Query aus Listing 7.30 um die Entity Publisher erweitert. Dabei spiegelt das Join.Objekt die Quelle und das Ziel des Joins wider:

EntityType<Book> book_ = em.getMetamodel().entity(Book.class);SingularAttribute<Book, Publisher> publisher_ = book_.getSingularAttribute("publisher", Publisher.class);

CriteriaBuilder cb = em.getCriteriaBuilder;CriteriaQuery<Book> q = cb.createQuery(Book.class);Root<Book> book = q.from(Book.class);q.select(book);TypedQuery<Book> tq = em.createQuery(q);List<Book> books = tq.getResultList();

Listing 7.30: Eine einfache Abfrage mit dem Criteria API

CriteriaQuery<Book> q = cb.createQuery(Book.class);Root<Book> book = q.from(Book.class);Join<Book, Publisher> pub = book.join(Book_.publisher);q.select(book);

Listing 7.31: Joins im Criteria API

Page 185: Jpa Mit en

7 – Datenbankabfragen mit JPA und Hibernate

184

Auch FetchJoins (Kapitel 8.1.1) können mit dem Criteria API abgebildet werden. Dazu findet die Methode fetch() des Root- und Join-Interfaces Anwendung.

Einschränken der Ergebnismenge

Das Einschränken der Ergebnismenge erfolgt mittels der where()-Methode der Criteria-Query, indem entsprechende Instanzen des Predicate-Interface übergeben werden. Sie werden über den CriteriaBuilder erzeugt. Listing 7.32 zeigt das einfache Beispiel aus Kapitel 7.2.4.

Mithilfe der get()-Methode wird ausgehend vom Root-Objekt bis zum gewünschten Attribut navigiert, wobei das statische Metamodell verwendet wird.

Modifikation der CriteriaQuery

Eine CriteriaQuery kann sowohl vor als auch nach dem Erzeugen und Ausführen von TypedQuerys verändert werden. Dies ist der wesentliche Vorteil des objektorientierten Ansatzes des Criteria API gegenüber der JPQL. Am Beispiel aus dem vorigen Abschnitt soll das entsprechend in Listing 7.33 demonstriert werden:

Es ist allerdings zu beachten, dass die erzeugten Predicate-Objekte an die CriteriaQuery gebunden sind und somit nicht in anderen CriteriaQuerys verwendet werden können.

// Select b from Book b where b.title = 'Book 9'CriteriaQuery<Book> q = cb.createQuery(Book.class);Root<Book> book = q.from(Book.class);q.select(book).where(cb.equal(book.get(Book_.title),"Book 9"));

Listing 7.32: Besipiel aus Kapitel 7.2.4

// Select b from Book b CriteriaQuery<Book> q = cb.createQuery(Book.class);Root<Book> book = q.from(Book.class);// where b.title = 'Book 9'Predicate predTitle = cb.equal(book.get(Book_.title),"Book 9");q.select(book).where(predTitle);List<Book> books = em.createQuery(q).getResultList();// where b.author = 'Author ONE'Predicate predAuthor = cb.equal(book.get(Book_.author),"Author ONE");q.where(predAuthor);books = em.createQuery(q).getResultList();

Listing 7.33: Modifikation einer CriteriaQuery

Page 186: Jpa Mit en

Zusammenfassung

JPA mit Hibernate 185

Dynamisches Metamodell und String-Referenzierung

Anhand des letzten Beispiels sollen nun die Unterschiede zur Verwendung des dynami-schen Metamodells und der String-Referenzierung gegenüber dem statischen Meta-modell in Listing 7.34 aufgezeigt werden.

7.7 ZusammenfassungIn diesem Kapitel wurden mehrere Möglichkeiten vorgestellt, um mit JPA schnell und effizient Abfragen zu formulieren. Dabei ist die Java Persistence Query Language (JPQL) eine sehr mächtige Abfragesprache, die im Vergleich zu SQL um Konzepte wie etwa das der polymorphen Abfrage erweitert wurde. JPA unterstützt dabei stets die Formulierung von Abfragen über SQL und bietet dadurch die Möglichkeit zur schrittweisen Migration von bestehenden SQL-basierten Lösungen. Bei der Neuentwicklung sollte aber, so weit dies möglich ist, nicht auf die nativen SQL-Abfragen zurückgegriffen werden, da hier-durch ein wesentliches Feature von JPA, nämlich die Datenbankunabhängigkeit, geop-fert wird.

Das Criteria API von Hibernate bietet im Vergleich zu SQL und JPQL einen komplett anderen Ansatz zur Formulierung von Datenbankabfragen. Es ermöglicht einen objekt-orientierten Weg zur Erzeugung einer Abfrage. Bedingungen werden nicht mehr per String-Verkettung aufgebaut, sondern durch Erzeugung von Objekten und das Setzen von Attributen. Der durch das Criteria API bereitgestellte Mechanismus des Query-by-Example bietet eine sehr einfache Möglichkeit, um komplexe Suchmasken zu realisieren, denn es wird einfach ein Beispielobjekt der entsprechenden Entity erzeugt und als Such-kriterium übergeben.

Das Criteria API von JPA ist mit dem von Hibernate vergleichbar. Jedoch kann man durch die Verwendung eines Metamodells ganz auf die Verwendung von Strings ver-zichten und somit komplett typsichere Abfragen definieren.

// Select b from Book b where b.title = 'Book 9'// Dynamisches MetamodellMetamodel mm = em.getMetamodel();EntityType<Book> book_ = mm.entity(Book.class);CriteriaQuery<Book> q = cb.createQuery(Book.class);Root<Book> book = q.from(Book.class);q.select(book).where(cb.equal(book.get(book_.getSingularAttribute("title", String.class)),"Book 9"));

// String-ReferenzierungCriteriaQuery<Book> q = cb.createQuery(Book.class);Root<Book> book = q.from(Book.class);q.select(book).where(cb.equal(book.get("title"),"Book 9"));

Listing 7.34: Dynamisches Metamodell und String-Referenzierung

Page 187: Jpa Mit en
Page 188: Jpa Mit en

JPA mit Hibernate 187

8 Fetching-Strategien und Caches

Viele Datenbankzugriffe sind häufig der Grund für Performanceprobleme. Hibernate ist als JPA-Provider „out of the box“ zwar sehr leistungsfähig, aber in bestimmten Fällen muss per Hand eingegriffen werden. Damit ist nicht gemeint, dass bei komplexen Abfragen oder Massen-Updates bzw. -Deletes mit native SQL gearbeitet werden muss. Vielmehr geht es darum, je nach Anwendungsfall beispielsweise eine geeignete Fetching-Strategie zu wäh-len oder einer Anwendung mit einem Second Level Cache zu schnelleren Zugriffen zu ver-helfen. In den nächsten Abschnitten werden die verschiedenen Fetching-Strategien des JPA und Hibernate vorgestellt. Weiterhin wird gezeigt, wie man mit dem Hibernate Query Cache und Second Level Cache die Performance einer Anwendung erheblich steigern kann.

8.1 Fetching-StrategienDie finale Spezifikation des JPA definiert zwei Fetching-Strategien:

� Lazy Load: Die Daten werden erst geladen, wenn ein Zugriff auf das Attribut oder das Objekt erfolgt.

� Eager Load: Die Daten werden sofort vollständig geladen.

Diese beiden Fetching-Strategien können optional beispielsweise bei Attributen

oder bei Beziehungen

angegeben werden.

Leider war man sich bei der Spezifikation von Lazy Load innerhalb der Expert Group nicht einig. Dadurch wurde dieses Thema in der Spezifikation relativ offen gehalten, konkret heißt dies, dass

� der Persistence Provider (wie z. B. Hibernate) nicht verpflichtet ist, Lazy Load über-haupt zu unterstützen und

� dadurch auch der Umfang einer Umsetzung von Lazy Load nur ungenau definiert ist.

@Basic(fetch = FetchType.LAZY)private String foo;

@OneToMany(fetch = FetchType.EAGER)private Set<Book> books = new HashSet<Book>();

Page 189: Jpa Mit en

8 – Fetching-Strategien und Caches

188

Hibernate unterstützt als JPA Persistence Provider Lazy Load mit der Einschränkung, dass Attribute oder Objekte nur nachgeladen werden können, wenn die Entity vom Enti-tyManager bzw. der Session verwaltet wird. Das heißt, ein Zugriff auf ein noch nicht geladenes Attribut am Client (außerhalb des EntityManagers oder der Hibernate-Ses-sion) führt zu einer Exception.

Zuerst soll allerdings in Listing 8.1 das erfolgreiche Nachladen gezeigt werden:

Bei der Definition der Beziehung books von Publisher wurde der Fetch-Typ nicht angege-ben, dennoch lädt Hibernate als Persistence Provider erst beim Zugrff via getBooks die ent-sprechenden Entities nach, da dies der Default für eine 1-zu-n-Beziehung ist (Tabelle 8.1).

@Entitypublic class Publisher implements Serializable { @OneToMany(mappedBy = "publisher") private List<Book> books;

...

Query jQuery = em.createQuery("select p from Publisher p"); List<Publisher> lAry = jQuery.getResultList(); for (Publisher p : lAry) { System.out.println(p); System.out.println("--Books--"); for (Book b : p.getBooks()) { System.out.println(b); } }

...

// LogausgabenHibernate: select p.myId, p.description from Publisher p[Publisher] description = "Publisher ONE"--Books--Hibernate: select b.publisher_myId, b.id, , b.ISBN, b.author, b.pages, b.publisher_myId, b.title from Book b where b.publisher_myId=? [Book] title = "Book 1" author = "Author ONE" ISBN = "1111"" pages = "123"[Book] title = "Book 3" author = "Author THREE" ISBN = "3333"" pages = "111"[Book] title = "Book 2" author = "Author TWO" ISBN = "2222"" pages = "132"...

Listing 8.1: Nachladen einer Beziehung

Page 190: Jpa Mit en

Fetching-Strategien

JPA mit Hibernate 189

In Listing 8.2 wird der EntityManger vor dem Nachladen der Book-Entities geschlossen, was zu einer LazyInitializationException führt.

Folgende Tabelle zeigt alle Annotationen, an denen die Fetching-Strategie bestimmt wer-den kann, mit den entsprechenden Default-Werten.

Beim Eager Loading werden, wie bereits erwähnt, die entsprechend markierten Referen-zen sofort mit geladen. Die durch Hibernate generierten SQL Statements werden nun nachfolgend betrachtet (Listing 8.3).

Query jQuery = em.createQuery("select p from Publisher p"); List<Publisher> lAry = jQuery.getResultList(); em.close(); for (Publisher p : lAry) { System.out.println(p); System.out.println("--Books--"); for (Book b : p.getBooks()) { System.out.println(b); } }

...// Logausgabenfailed to lazily initialize a collection of role: booksonline.bo.Publisher.books, no session or session was closed

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: booksonline.bo.Publisher.books, no session or session was closed

Listing 8.2: Nachladen von Entities bei geschlossenem EntityManager

Annotation Defaultwert

@Basic(fetch = FetchType.EAGER/LAZY) FetchType.EAGER

@OneToOne(fetch = FetchType.EAGER/LAZY) FetchType.EAGER

@ManyToOne(fetch = FetchType.EAGER/LAZY) FetchType.EAGER

@OneToMany(fetch = FetchType.EAGER/LAZY) FetchType.LAZY

@ManyToMany(fetch = FetchType.EAGER/LAZY) FetchType.LAZY

Tabelle 8.1: Fetching-Strategien

Query jQuery = em.createQuery("select b from Book b");List<Book> lAry = jQuery.getResultList();for (Book b : lAry) {

Listing 8.3: Durch Hibernate generierte SQL Statements für Eager Loading

Page 191: Jpa Mit en

8 – Fetching-Strategien und Caches

190

Für jede aufzulösende Referenz wird, wie im Beispiel gezeigt, ein separates select-State-ment ausgeführt. Bei vielen Entities mit zahlreichen Referenzen kann dies einen ernstzu-nehmenden Performanceverlust darstellen. Das hier beschriebene Problem ist auch als N+1-Problem bekannt. Dabei steht die 1 für das eigentliche select zum Auswählen der gewünschten Entity und das N für die nachzuladenden Referenzen.

In JPA und Hibernate ist dieses Verhalten der Default und wird als Select-Fetching bezeichnet. Im Folgenden sollen die weiteren Fetching-Strategien vorgestellt werden. In JPA ist als weitere Strategie das Join-Fetching spezifiziert. Hibernate bietet darüber hin-aus noch das Batch- und Subselect-Fetching

8.1.1 Fetch Joins

Mit Fetch Joins lässt sich die Anzahl der Datenbankzugriffe auf einen einzigen redu-zieren, indem mittels eines Joins durch Beziehungen verknüpfte Entites im selben State-ment mitgeladen werden. Dabei sind die durch den Fetch Join referenzierten Entities jedoch nicht Teil der Ergebnismenge (Resultset), sondern werden, im Fall von Eager Loa-ding, automatisch initialisiert und den abgefragten Entites aus dem Resultset zugewie-sen. Ist für die Beziehung Lazy Loading definiert, so wird erst beim Zugriff bzw. bei der Navigation zur referenzierten Entity diese instantiiert und bereitgestellt.

Join Fetching eignet sich vor allem für Anwendungsfälle, bei denen im voraus bekannt ist, dass die Objekte der Assoziation benötigt werden. Dabei sollte darauf geachtet wer-den, dass es sich nicht um Beziehungen mit allzu großer Ergebnismenge handelt, da die Performance aufgrund der Bildung des kartesischen Produkts durch den Join, vor allem bei der Auflösung mehrerer Beziehungen, stark einbrechen kann.

Ein Fetch Join ist per Default ein Inner Join. Ein Inner Join verknüpft zwei Datensätze, bei denen ein Feld jeweils denselben Wert hat (Kapitel 7.2.6).

System.out.println(b + " | " + b.getPublisher());}...// Logausgaben (lesbar gemacht durch Umbenennen der Variablen)Hibernate: select b.id, b.ISBN, b.author, b.pages, b.publisher_myId, b.title from Book bHibernate: select p.myId, p.description from Publisher p where p.myId=?Hibernate: select p.myId, p.description from Publisher p where p.myId=?Hibernate: select p.myId, p.description from Publisher p where p.myId=?

[Book] title = "Book 1" author = "Author ONE" ISBN = "1111"" pages = "123" | [Publisher] description = "Publisher ONE"[Book] title = "Book 2" author = "Author TWO" ISBN = "2222"" pages = "132" | [Publisher] description = "Publisher ONE"...

Listing 8.3: Durch Hibernate generierte SQL Statements für Eager Loading (Forts.)

Page 192: Jpa Mit en

Fetching-Strategien

JPA mit Hibernate 191

In Listing 8.4 werden alle Entities vom Typ Book mit den entsprechend referenzierten Entites Publisher mittels eines Fetch Joins geladen.

<

Wie zu erwarten, wurde der Fetch Join als Inner Join aufgelöst, und bei der Ausgabe der Information zu den Entities ist kein weiteres Nachladen erforderlich.

Im Beispiel in Listing 8.5 sollen alle Entities vom Typ Publisher und deren dazugehörige Book Entities mittels eines Fetch Joins geladen werden.

Query jQuery = em.createQuery("select b from Book b join fetch b.publisher");List<Book> lAry = jQuery.getResultList();for (Book b : lAry) { System.out.println(b + " | " + b.getPublisher());}//LogausgabenHibernate: select b.id, p.myId, b.ISBN, b.author, b.pages, b.publisher_myId, b.title, p.description from Book b inner join Publisher p on b.publisher_myId=p.myId

[Book] title = "Book 3" author = "Author THREE" ISBN = "3333"" pages = "111" | [Publisher] description = "Publisher ONE"[Book] title = "Book 2" author = "Author TWO" ISBN = "2222"" pages = "132" | [Publisher] description = "Publisher ONE"[Book] title = "Book 1" author = "Author ONE" ISBN = "1111"" pages = "123" | [Publisher] description = "Publisher ONE"[Book] title = "Book 9" author = "Author TEN" ISBN = "0000"" pages = "230" | [Publisher] description = "Publisher TWO"...

Listing 8.4: Verwendung eines Fetch Joins

Query jQuery = em.createQuery("select p from Publisher p join fetch p.books");List<Publisher> lAry = jQuery.getResultList(); for (Publisher p : lAry) { System.out.println(p); System.out.println("--Books--"); for (Book b : p.getBooks()) { System.out.println(b); } }

// LogausgabenHibernate: select p.myId, b.id, p.description, b.ISBN, b.author, b.pages, b.publisher_myId, b.title, b.publisher_myId, b.id

Listing 8.5: Verwendung eines Fetch Joins mit „Nebeneffekt“

Page 193: Jpa Mit en

8 – Fetching-Strategien und Caches

192

Das durch den Fetch Join generierte Statement wird, wie zu erwarten, mit einem Inner Join generiert. Es ist allerdings zu beachten, dass im Resultset mehrere Einträge gleicher Entities enthalten sind. Dies ist ein „Nebeneffekt“ des Joins. Aus diesem Grund sollte man bei einem Fetch Join auf die Angabe von setMaxResults() und setFirstResult() ver-zichten. Des Weiteren kann man für die Referenzen, die mittels fetch geladen werden, sollen keine Identifikationsvariable definieren und sie somit nicht in anderen Teilen der Abfrage, wie beispielsweise der Where-Klausel, verwenden.

Mit dem JPA kann man den Fetch Join auch als so genannten Left Join bzw. Left Outer Join definieren. Dabei werden auch Datensätze der ersten Tabelle der Ergebnismenge hinzugefügt, die keine entsprechenden Datensätze in der zweiten Tabelle haben (Kapitel 7.26).

Die Syntax für einen Fetch Join kann im Allgemeinen wie folgt definiert werden:

Mit den Fetch Joins steht bereits eine gute Möglichkeit für die Optimierung des N+1-Pro-blems zur Verfügung. In den folgenden Abschnitten sollen die weiteren Fetch-Strategien vorgestellt werden, die nicht in JPA definiert, sondern mit Hibernate möglich sind.

8.1.2 Batch Fetching mit Hibernate

Eine weitere Möglichkeit der Optimierung des N+1-Problems ist das Batch Fetching. Dabei werden Referenzen, je nach angegebener Batchgröße, gemeinsam in einem Select geladen. Das Batch Fetching wird nicht auf Abfrageebene spezifiziert, sondern direkt pro Entity bzw. Collection festgelegt. Im Beispiel in Listing 8.6 wird Batch Fetching für die Entity Publisher definiert. Dabei sollen jeweils drei Entities vom Typ Publisher pro selectabgerufen werden.

from Publisher p inner join Book b on p.myId=b.publisher_myId order by b.author asc...[Publisher] description = "Publisher THREE"--Books--[Book] title = "Book 8" author = "Author EIGHT" ISBN = "8888"" pages = "173"[Book] title = "Book 9" author = "Author NINE" ISBN = "9999"" pages = "60"...[Publisher] description = "Publisher THREE"--Books--[Book] title = "Book 8" author = "Author EIGHT" ISBN = "8888"" pages = "173"[Book] title = "Book 9" author = "Author NINE" ISBN = "9999"" pages = "60"...

fetch_join ::= [ LEFT [OUTER] | INNER ] JOIN FETCH association_path

Listing 8.5: Verwendung eines Fetch Joins mit „Nebeneffekt“ (Forts.)

Page 194: Jpa Mit en

Fetching-Strategien

JPA mit Hibernate 193

An den SQL-Abfragen ist erkennbar, dass das N+1-Problem nunmehr nur noch ein N/Batchgröße+1-Problem ist.

Beim Batch Fetching werden alle Referenzen des entsprechenden Entity-Typs für alle im EntityManager geladenen Entities berücksichtigt und gemeinsam geladen.

Auf Collections lässt sich Batch Fetching ebenso anwenden, indem die Annotation @org.hibernate.annotations.BatchSize bei der Definition der Beziehung mit definiert wird.

@org.hibernate.annotations.BatchSize(size=3)public class Publisher

...

Query jQuery = em.createQuery("select b from Book b");List<Book> lAry = jQuery.getResultList();for (Book b : lAry) { System.out.println(b +" | "+ b.getPublisher());}// LogausgabenHibernate: select b.id, b.ISBN, b.author, b.pages, b.publisher_myId, b.title from Book bHibernate: select p.myId, p.description from Publisher p where p.myId in (?, ?, ?)

Listing 8.6: Abfrage mit Batch Fetching

public class Publisher {... @OneToMany(mappedBy = "publisher", cascade={CascadeType.ALL}) @BatchSize(size=2) private List<Book> books;...Query jQuery = em.createQuery("select p from Publisher p");List<Publisher> lAry = jQuery.getResultList();for (Publisher p : lAry) { System.out.println(p); System.out.println("--Books--"); for (Book b : p.getBooks()) { System.out.println(b); }}

Listing 8.7: Verwendung von @BatchSize für 1-zu-n-Beziehung

Page 195: Jpa Mit en

8 – Fetching-Strategien und Caches

194

In Listing 8.7 wird für die @OneToMany-Referenz auf Books eine Batch-Größe von zwei defi-niert. Dadurch werden beim Aufruf von getBooks() alle Bücher der ersten zwei Publisher geladen.

8.1.3 Subselect-Fetching mit Hibernate

Subselect Fetching kann lediglich für „zu-n“-Beziehungen definiert werden. Im Gegen-satz zu Batch Fetching werden alle, und nicht nur eine definierte Anzahl, Referenzen geladen. Dies wird mittels eines Subselect in einem zusätzlichen Statement erreicht, das alle entsprechenden IDs zum Abrufen der Referenzen liefert. In Listing 8.8 wird die Ver-wendung von Subselect Fetching gezeigt.

...// LogausgabenHibernate: select p.myId, p.description from Publisher p[Publisher] description = "Publisher ONE"

--Books--Hibernate: select b.publisher_myId, b.id, b.ISBN, b.author, b.pages, b.publisher_myId, b.title from Book b where b.publisher_myId in (?, ?) ...[Book] title = "Book 9" author = "Author TEN" ISBN = "0000"" pages = "230"[Publisher] description = "Publisher THREE"--Books--Hibernate: select b.publisher_myId, b.id, b.ISBN, b.author, b.pages, b.publisher_myId, b.title from Book b where b.publisher_myId=?[Book] title = "Book 8" author = "Author EIGHT" ISBN = "8888"" pages = "173"[Book] title = "Book 9" author = "Author NINE" ISBN = "9999"" pages = "60"

public class Publisher {... @OneToMany(mappedBy = "publisher", cascade={CascadeType.ALL}) @Fetch(FetchMode.SUBSELECT) private List<Book> books;...Query jQuery = em.createQuery("select p from Publisher p");List<Publisher> lAry = jQuery.getResultList();for (Publisher p : lAry) { System.out.println(p); System.out.println("--Books--"); for (Book b : p.getBooks()) {

Listing 8.8: Verwendung von Subselect Fetching

Listing 8.7: Verwendung von @BatchSize für 1-zu-n-Beziehung (Forts.)

Page 196: Jpa Mit en

Hibernate Query und Second Level Cache

JPA mit Hibernate 195

8.2 Hibernate Query und Second Level CacheFür sich wiederholende identische Abfragen macht es Sinn, den Hibernate Query Cachezu aktivieren. Zu beachten ist, dass der Query Cache nur die IDs (Primärschlüssel) in der Ergebnisliste einer bestimmten Abfrage hinterlegt. Das heißt, um auch wirklich mehr Performance zu erreichen, muss ein Second Level Cache vorhanden sein, der die Entities für die IDs im Query Cache bereitstellen kann.

Um den Query Cache zu benutzen, muss dieser in der Hibernate-Konfiguration aktiviert werden. Dies erfolgt über die Property hibernate.cache.use_query_cache, die in der per-sistence.xml auf true gesetzt werden muss.

Des Weiteren muss auf Abfragen setCacheable(true) aufgerufen werden, damit diese dann auch gecacht werden. Folgendes Listing zeigt eine Abfrage mit aktiviertem Query Cache:

Caches werden benutzt, um Datenbankanwendungen zu optimieren. Der Cache hält bereits geladene Daten, und Datenbankzugriffe sind nur nötig, wenn die gesuchten Daten im Cache nicht enthalten sind. Hibernate nutzt einen First Level Cache und optio-nal einen Second Level Cache. Der First Level Cache ist in der Hibernate Session, bzw. dem EntityManager enthalten, und dessen Lebensdauer beschränkt sich auf die der Ses-sion bzw. die des EntityManagers. Mit dem First Level Cache soll erreicht werden, dass möglichst wenig SQL-Statements innerhalb einer Transaktion generiert werden. Wenn

System.out.println(b); }}...// LogausgabenHibernate: select p.myId, p.description from Publisher p[Publisher] description = "Publisher ONE"--Books--Hibernate: select b.publisher_myId, b.id, b.id, b.ISBN, b.author, b.pages, b.title from Book b where b.publisher_myId in (select p.myId from Publisher p) [Book] title = "Book 1" author = "Author ONE" ISBN = "1111"" pages = "123"[Book] title = "Book 3" author = "Author THREE" ISBN = "3333"" pages = "111"[Book] title = "Book 2" author = "Author TWO" ISBN = "2222"" pages = "132"[Publisher] description = "Publisher TWO"--Books--[Book] title = "Book 5" author = "Author FIFE" ISBN = "5555"" pages = "102"...

List<Book> books = session.createQuery("select b from Book b") .setCacheable(true).list();

Listing 8.8: Verwendung von Subselect Fetching (Forts.)

Page 197: Jpa Mit en

8 – Fetching-Strategien und Caches

196

beispielsweise eine Entity in einer Transaktion mehrfach geändert wird, generiert Hiber-nate nur ein Update-Statement am Ende der Transaktion. Der Second Level Cache resi-diert oberhalb der Hibernate Session bzw. des EntityManagers, das heißt, die Entities im Second Level Cache stehen der gesamten Anwendung zur Verfügung.

Neu in JPA 2.0

Für die Grundfunktionalität und die Konfiguration des Second Level Cache wird in JPA 2.0 ein einheitliches API zur Verfügung gestellt. Jedoch muss der Persistence Provider keine Implementierung eines Second Level Cache ent-halten.

In der persistence.xml wird das Verhalten des Second Level Cache über die Property shared-cache-mode gesteuert. Es stehen folgende Auswahlmöglich-keiten zur Verfügung:

� ALL: Das Caching wird für alle Entites aktiviert

� NONE: Das Caching ist deaktiviert

� ENABLE_SELECTIVE: Es werden nur jene Entites gecacht, die mit der Anno-tation @Cacheable(true) markiert sind

� DISABLE_SELECTIVE: Entities mit der Annotation @Cacheable(false) werden nicht gecacht

� UNSPECIFIED: Es werden die Einstellungen des jeweiligen Persistence Pro-viders verwendet

Für das Lesen aus dem und das Schreiben in den Cache können die Einstel-lungen javax.persistence.cache.retrieveMode und javax.persistence.cache. storeMode als Property bzw. Hint dem EntityManager oder der Query überge-ben werden. In der Enumeration javax.persistence.CacheRetrieveMode sind folgende Einstellungen definiert:

� USE : Der Second Level Cache wird verwendet, allerdings wird beim Aus-führen von refresh immer aus der Datenbank gelesen

� BYPASS: Die Daten der Entity werden aus der Datenbank gelesen und der Second Level Cache wird ignoriert

Die Enumeration javax.persistence.CacheStoreMode enthält für das Speichern von Entites im Cache folgende möglichen Einstellungen:

� USE: Beim Lesen oder Schreiben einer Entity aus oder in die Datenbank wird diese im Second Level Cache gespeichert; ist die Entity bereits im Cache vorhanden, findet keine Aktualisierung statt

� BYPASS: Die Entitiy wird nicht im Second Level Cache gespeichert

� REFRESH: Wie USE, jedoch wird die Entity auch dann aktualisiert, wenn sie sich bereits im Second Level Cache befindet

i

Page 198: Jpa Mit en

Hibernate Query und Second Level Cache

JPA mit Hibernate 197

8.2.1 Strategien und Konfiguration

Es stehen insgesamt vier verschiedene Caching-Strategien zur Verfügung:

� Read-Only: Das ist die einfachste und zugleich performanteste Strategie. Sie kann eingesetzt werden, wenn ausschließlich lesend auf die Datenbank zugegriffen wird.

� Read-Write: Bei der Strategie read-write dürfen Daten auch geändert werden. Es ist zu beachten, dass diese Strategie nicht mit dem Isolation Level Serializable1 verwend-bar ist und dass die Session oder der EntityManager am Transaktionsende mit close()beendet werden müssen.

� Nonstrict Read-Write: Mit nonstrict read-write kann nicht garantiert werden, dass zwei Transaktionen nicht gleichzeitig denselben Datensatz ändern. Daher ist die Strategie geeignet, wenn Daten nur selten geändert werden und es sehr unwahr-scheinlich ist, dass zwei Transaktionen dieselben Daten ändern. Wie bei read-write müssen die Session oder der EntityManager am Transaktionsende geschlossen wer-den.

� Transactional: Kann nur in einer JTA-Umgebung verwendet werden und entspricht dem Isolation Level Repeatable Read2.

Die Caching-Strategie kann auf Entity-Ebene und für Collections konfiguriert werden. Dazu kommt die Annotation @org.hibernate.annotations.Cache zum Einsatz.

Der Cache Provider wird in der persistence.xml definiert, indem die Klasse des Cache Providers angegeben wird.

1. Siehe Kapitel 6.2 Isolation Level2. Siehe Kapitel 6.2 Isolation Level

@Entity@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)public class Publisher { ... @OneToMany @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) public Set<Book> getBooks() { return books; } ...}

<persistence-unit name="booksonlinePU" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> ... <properties> ...

Page 199: Jpa Mit en

8 – Fetching-Strategien und Caches

198

8.2.2 Second Level Cache Provider

Tabelle 8.2 zeigt eine Übersicht über die Cache Provider und die Caching-Strategien, die sie unterstützen.345

8.3 ZusammenfassungWie man in diesem Kapitel erfahren hat, gibt es keine für alle Anwendungsfälle perfekt passende Fetching-Strategie. Grundsätzlich wird empfohlen, alle Beziehungen als Lazy Loading zu definieren und bei bestimmten Szenarien eine entsprechende, hier vorge-stellte Fetching-Strategie zu wählen. Ähnlich wird bei den Caching-Strategien verfahren. Es macht nicht viel Sinn, alle Entities mit einem Second Level Cache zu versehen. Viel-mehr muss erst durch Belastungstests herausgefunden werden, wo ein Second Level mit größtmöglichem Performancegewinn eingesetzt werden kann.

<property name="hibernate.cache.provider_class" value="org.hibernate.cache.OSCacheProvider"/> </properties> </persistence-unit>

3. EHCache: http://ehcache.sourceforge.net4. OSCache: http://www.opensymphony.com/oscache/5. JBoss Cache: http://www.jboss.org/products/jbosscache

Cache Provider Read-Only Read-Write Nonstrict Read-Write Transactional

EHCache3 Ja Ja Ja Nein

OSCache4 Ja Ja Ja Nein

JBoss Cache5 Ja Nein Nein Ja

Tabelle 8.2: Cache Provider und deren unterstützte Strategien

Page 200: Jpa Mit en

JPA mit Hibernate 199

9 Hibernate Types

Hibernate Types bilden den Zwischenschritt zwischen den Java- und den SQL-Typen. Mit der Annotation @org.hibernate.annotations.Type kann der verwendete Hibernate Type für Attribute oder Entities spezifiziert werden. Dabei ist die Angabe immer optio-nal, da Hibernate für die meisten Java-Typen bereits sinnvolle Default-Mapping-Typen enthält und sie automatisch verwendet.

In diesem Kapitel werden zu Beginn die in Hibernate bereits vorhandenen Typen kurz vorgestellt. Im zweiten Teil wird dann anhand eines Beispiels gezeigt, wie eigene, so genannte User Types, definiert und verwendet werden können.

9.1 Hibernate Mapping TypesIn Hibernate sind bereits Mapping Types (Tabelle 9.1) für eine Vielzahl von Java-Typen (primitive wie auch Klassen) enthalten. Die Hibernate Mapping Types werden stets klein geschrieben.

Hibernate Mapping Types Java-Typ Standard-SQL-Datentyp

string java.lang.String VARCHAR

short short oder java.lang.Short SMALLINT

integer int oder java.lang.Integer INTEGER

long long oder java.lang.Long BIGINT

float float oder java.lang.Float FLOAT

double double oder java.lang.Double DOUBLE

big_decimal java.math.BigDecimal NUMERIC

big_integer java.math.BigInteger NUMERIC

character java.lang.String CHAR(1)

byte byte oder java.lang.Byte TINYINT

boolean boolean oder java.lang.Boolean BIT

yes_no boolean oder java.lang.Boolean CHAR(1) ('Y' oder 'N')

true_false boolean oder java.lang.Boolean CHAR(1) ('T' oder 'F')

date java.util.Date oder java.sql.Date DATE

time java.util.Date oder java.sql.Time TIME

Tabelle 9.1: Hibernate Mapping Types

Page 201: Jpa Mit en

9 – Hibernate Types

200

Für die Schemagenerierung übersetzt Hibernate die SQL-Standardtypen mithilfe des SQL-Dialekt-Mechanismus, mit dem beliebige Datenbanken angebunden werden kön-nen. Normalerweise hat man also keine Probleme durch den Unterschied zwischen den Standard-SQL-Typen und den datenbankspezifischen Datentypen bei der Verwendung von Hibernate zu erwarten.

Für date, time, timestamp, calendar, calendar_date, serializable und binary stehen auch spezielle Mapping-Typen (imm_date, imm_time, etc.) zur Verfügung, bei denen Hibernate spezielle Optimierungen unter der Annahme vornimmt, dass die Objekte unveränderlich sind. Bei Verwendung dieser Mapping-Typen muss darauf geachtet werden, dass die Objekte nicht verändert werden, sondern stets das komplette Objekt ausgetauscht wird.

Alle hier vorgestellten Typen mit Ausnahme von binary, blob und clob können als ID-Fel-der von Entities verwendet werden. Außerdem unterstützen alle Typen Null-Semantik, das heißt, es ist möglich, die entsprechenden Attribute auf den Wert null zu setzen.

Für alle per Default in Hibernate enthaltenen Mapping Types sind entsprechende Kon-stanten unter org.hibernate.Hibernate definiert, so kann z. B. der Mapping Type für Inte-ger unter org.hibernate.Hibernate.INTEGER im Quelltext verwendet werden.

Timestamp java.util.Date oder java.sql.Timestamp

TIMESTAMP

Calendar java.util.Calendar TIMESTAMP

calendar_date java.util.Calendar DATE

Binary byte[] VARBINARY oder BLOB

Text java.lang.String CLOB

Serializable java.io.Serializable (beliebige Klasse welche das Interface implementiert)

VARBINARY oder BLOB

Clob java.sql.Clob CLOB

Blob java.sql.Blob BLOB

Class java.lang.Class VARCHAR

Locale java.util.Locale VARCHAR

Timezone java.util.TimeZone VARCHAR

Currency java.util.Currency VARCHAR

Hibernate Mapping Types Java-Typ Standard-SQL-Datentyp

Tabelle 9.1: Hibernate Mapping Types (Forts.)

Page 202: Jpa Mit en

Benutzerdefinierte Mapping Types

JPA mit Hibernate 201

9.2 Benutzerdefinierte Mapping TypesNeben der Benutzung der Standard-Hibernate-Mapping-Types besteht auch die Mög-lichkeit, eigene Mapping Types, so genannte custom value types, zu definieren. Diese benutzerdefinierten Mapping Types machen Sinn, wenn Werte-Types, also Types, die keine vollwertigen Entities sind, an mehreren Stellen innerhalb eines Domain Models verwendet werden. Des Weiteren lässt sich z. B. ein Mapping Type erzeugen, um einen Integer-Wert als VARCHAR in der Datenbank abzulegen und so eine Alternative zum Default-Mapping für Integer zu haben. Natürlich lassen sich auch Mapping-Typen defi-nieren, die aus mehreren Feldern bestehen und auch in der Datenbank mehrere Spalten belegen.

Die einfachste Art, einen neuen Mapping Type zu definieren, ist die Implementierung von org.hibernate.UserType. In diesem Interface sind alle Methoden zusammengefasst, die implementiert werden müssen, um einen neuen Mapping-Typ zu definieren, mit dem die Hibernate Engine umgehen kann.

Als Beispiel dient eine E-Mail-Adresse, deren Domain- und Username getrennt gespei-chert werden sollen. Sie wird durch eine eigene Klasse repräsentiert, in der die beiden Teile der E-Mail-Adresse getrennt abgelegt werden (Listing 9.1).

package booksonline.bo;...public class EmailAddress implements Serializable { private static final long serialVersionUID = 1L; private String userName; private String domainName; public EmailAddress(String domainName, String userName) { this.domainName = domainName; this.userName = userName; } public String getDomainName() { return domainName; } public void setDomainName(String domainName) { this.domainName = domainName; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; }}

Listing 9.1: Die Klasse „EmailAddress”

Page 203: Jpa Mit en

9 – Hibernate Types

202

Die Klasse EmailAddressUserType implementiert das UserType-Interface und ermöglicht so die Einbindung der Klasse EmailAddress als benutzerdefinierten Mapping Type (Listing 9.2).

package booksonline.bo;...public class EmailAddressUserType implements UserType { private static final int[] TYPES = { Types.VARCHAR, Types.VARCHAR }; public int[] sqlTypes() { return TYPES; } public Class returnedClass() { return EmailAddress.class; } public boolean equals(Object x, Object y) { if (x==y) return true; if (x==null || y==null) return false; return ( (EmailAddress) x ).getDomainName().equals( ( (EmailAddress) y ).getDomainName() ) && ( (EmailAddress) x ).getUserName().equals( ( (EmailAddress) y ).getUserName() ); } public int hashCode(Object x) throws HibernateException { EmailAddress a = (EmailAddress) x; return a.getDomainName().hashCode() + 31 * a.getUserName().hashCode(); } public Object assemble(Serializable cached, Object owner) { return deepCopy(cached); } public Object deepCopy(Object x) { if (x==null) return null; EmailAddress in = (EmailAddress) x; EmailAddress c = new EmailAddress(in.getDomainName(), in.getUserName()); return c; } public Serializable disassemble(Object value) { return (Serializable) deepCopy(value); } public boolean isMutable() { return true;

Listing 9.2: Die Klasse „EmailAddressUserType”

Page 204: Jpa Mit en

Benutzerdefinierte Mapping Types

JPA mit Hibernate 203

Um einen benutzerdefinierten Mapping-Typ bereitzustellen, müssen folgende Metho-den des Interfaces UserType implementiert werden:

� public int[] sqlTypes(): Liefert die SQL-Datentypen, die von diesem Mapping-Typ verwendet werden. Die möglichen SQL-Datentypen sind in java.sql.Types definiert. Hibernate verwendet diese Information u. a. für die Generierung des Datenbank-schemas. Es wird ein Array zurückgegeben, da sich ein Mapping auch auf mehrere Spalten beziehen kann.

� public Class returnedClass(): Liefert die Klasse, die durch diesen Mapping-Typ gemappt wird. Das Objekt, das von nullSafeGet() zurückgeliefert wird, muss eine Instanz dieser Klasse sein.

� public boolean equals(Object x, Object y): Vergleicht die beiden Objekte auf Gleich-heit der persistenten Attribute.

� public int hashCode(Object x): Liefert einen Hashcode, der konsistent mit dem Ergeb-nis von equals() ist.

� public Object assemble(Serializable cached, Object owner): Diese Methode wird auf-gerufen, um ein Objekt wiederherzustellen, das aus dem Cache geladen wurde. Han-delt es sich um ein Objekt, dessen Attribute veränderbar sind, sollte eine Kopie des Objekts zurückgegeben werden. Die Entity, die das aktuelle Objekt referenziert, wird als Parameter owner übergeben.

} public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException { String dn = (String) Hibernate.STRING.nullSafeGet(rs, names[0]); String un = (String) Hibernate.STRING.nullSafeGet(rs, names[1]); return ( dn==null && un==null ) ? null : new EmailAddress( dn, un ); } public void nullSafeSet(PreparedStatement st, Object v, int index) throws HibernateException, SQLException { String dn = (v==null) ? null : ((EmailAddress)v).getDomainName(); String un = (v==null) ? null : ((EmailAddress)v).getUserName(); Hibernate.STRING.nullSafeSet(st, dn, index); Hibernate.STRING.nullSafeSet(st, un, index+1); } public Object replace(Object original, Object target, Object owner) throws HibernateException { return original; }}

Listing 9.2: Die Klasse „EmailAddressUserType” (Forts.)

Page 205: Jpa Mit en

9 – Hibernate Types

204

� public Serializable disassemble(Object value): Wird aufgerufen, bevor ein Objekt in den Cache verschoben wird. Bei Objekten, deren Attribute veränderbar sind, sollte eine Kopie zurückgegeben werden.

� public Object deepCopy(Object x): Liefert eine Kopie des übergebenen Objekts. Even-tuell referenzierte Entities oder Collections dürfen nicht kopiert werden. Bei unverän-derlichen Klassen muss keine Kopie erzeugt werden.

� public boolean isMutable(): Gibt an, ob die Klasse, auf die sich dieser UserType bezieht, veränderbar ist. Hibernate kann einige Performanceoptimierungen durch-führen, für den Fall, dass es sich um unveränderliche Objekte handelt.

� public Object nullSafeGet(ResultSet rs, String[] names, Object owner): Diese Methode liest die Ergebnisse einer Query aus dem JDBC-ResultSet und erzeugt dar-aus ein Objekt der Klasse, die gemappt werden soll. Die Methode muss mit null-Wer-ten umgehen können. Die Entity, die das aktuelle Objekt referenziert, wird als Parameter owner übergeben.

� public void nullSafeSet(PreparedStatement st, Object value, int index): Diese Methode liest die Attribute des Objekts value und trägt sie in das JDBC PreparedStatement ein. Das Einfügen der Attribute muss mit dem Index index beginnen.

Die neue Klasse EmailAddress wird in der Entity UserWithComplexEmail verwendet (Listing 9.3).

Innerhalb der Klasse UserWithComplexEmail wird emailAddress durch eine Instanz der Klasse EMailAddress gemappt. Mithilfe der Annotation @org.hibernate.annotations.Typewird die Mapping-Klasse angegeben, die Hibernate für den Umgang mit EMailAddressbenötigt. In der Annotation @org.hibernate.annotations.Columns werden mit @javax.per-sistence.Column die Namen der Tabellenspalten definiert, auf die die Attribute der Klasse EMailAddress gemappt werden.

@Entitypublic class UserWithComplexEmail implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstname; private String lastname; @Type(type="booksonline.bo.EmailAddressUserType") @Columns(columns = { @Column(name="dn"), @Column(name="un") }) private EmailAddress emailAddress; ...

Listing 9.3: Die Klasse „UserWithComplexEmail“

Page 206: Jpa Mit en

Benutzerdefinierte Mapping Types

JPA mit Hibernate 205

Das Interface UserType ist nur für einfache Mappings gedacht, die sich rein aus SQL-Typen zusammensetzen.

Für komplexere Mapping-Typen, deren Attribute auch aus bereits vorhandenen Hiber-nate Types bestehen können, muss statt dem Interface org.hibernate.usertype.UserTypedas etwas umfangreichere Interface org.hibernate.usertype.CompositeUserType imple-mentiert werden.

Listing 9.4 zeigt die Klasse EmailAddressCompositeUserType, die das Interface Composite-UserType implementiert.

public class EmailAddressCompositeUserType implements CompositeUserType { private static final Type[] TYPES = { Hibernate.STRING, Hibernate.STRING }; private static final String[] PROPERTY_NAMES = new String [] { "domainName", "userName" }; public Class returnedClass() {...} public boolean equals(Object x, Object y) {...} public int hashCode(Object x) throws HibernateException {...} public Object assemble(Serializable c, SessionImplementor s, Object owner) { ... } public Object deepCopy(Object x) {...} public Serializable disassemble(Object value, SessionImplementor s) {...} public boolean isMutable() {...} public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor s, Object owner) throws HibernateException, SQLException {...} public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor s) throws HibernateException, SQLException {...} public Object replace(Object original, Object target, SessionImplementor s, Object owner) throws HibernateException { return original; }

Listing 9.4: Die Klasse „EmailAddressCompositeUserType”

Page 207: Jpa Mit en

9 – Hibernate Types

206

Viele Methoden von EmailAddressCompositeUserType sind identisch mit jenen von Email-AddressUserType (z. B. equals, hashCode, ...). Bei einigen ist lediglich die Signatur leicht ver-ändert, da der zusätzliche Parameter SessionImplementor übergeben wird, mit dem man Zugriff auf die Hibernate Engine erhält, um z. B. Subqueries innerhalb der Implementie-rung des User Types durchzuführen. Die konkrete Implementierung dieser Methoden in EmailAddressCompositeUserType ist allerdings identisch zu EmailAddressUserType.

Folgende Methoden mussten zusätzlich im Vergleich zu EmailAddressUserType implemen-tiert werden:

� public String[] getPropertyNames(): Liefert die Namen der Attribute des Mapping-Typs.

� public Type[] getPropertyTypes(): Liefert die Typen der Attribute. Die Typen werden, im Unterschied zu UserType, nicht mehr als int, sondern als org.hibernate.type.Typefestgelegt.

public String[] getPropertyNames() { return PROPERTY_NAMES; }

public Type[] getPropertyTypes() { return TYPES; }

public Object getPropertyValue(Object component, int property) throws HibernateException { if (property == 0) return ((EmailAddress) component).getDomainName(); else return ((EmailAddress) component).getUserName(); } public void setPropertyValue(Object component, int property, Object value) throws HibernateException { EmailAddress address = (EmailAddress) component; if (property == 0) address.setDomainName((String) value); else address.setUserName((String) value); }}

Listing 9.4: Die Klasse „EmailAddressCompositeUserType” (Forts.)

Page 208: Jpa Mit en

Benutzerdefinierte Mapping Types

JPA mit Hibernate 207

� public Object getPropertyValue(Object component, int property): Liefert den aktuellen Wert eines Attributs von component. Der Parameter component muss eine Instanz jener Klasse sein, auf die sich der Mapping-Typ bezieht. In unserem Beispiel also Email-Address. Zu beachten ist, dass die einzelnen Attribute nicht anhand des Namens, son-dern anhand eines Index übergeben werden. Dieser Index entspricht dem Index des Attributs innerhalb des Arrays, das von getPropertyTypes() zurückgegeben wird.

� public void setPropertyValue(Object component, int property, Object value): Setzt das Attribut mit dem Index property auf den Wert value.

� public Object replace(Object original, Object target, SessionImplementor session, Object owner): Diese Methode wird während dem Merge von Detached-Objekten aufgerufen. Beim Merge wird der Paramter target innerhalb der Entity durch origi-nal ersetzt. Bei unveränderlichen User Types kann einfach original zurückgegeben werden. Bei veränderbaren reicht es meist, eine Kopie von original zurückzugeben.

� Die Verwendung eines CompositeUserType in Bezug auf die Deklaration von Meta-daten in Annotations oder im XML-Format ist identisch zu UserType. Es muss ledig-lich beachtet werden, dass die Reihenfolge der deklarierten Spalten (@Column) mit der Reihenfolge von getPropertyNames()und getPropertyTypes() übereinstimmt.

Der neu erstellte EmailAddressCompositeUserType wird analog zum UserType in der Entity verwendet. In Listing 9.5 werden anhand des Domain-Namens Benutzer gesucht, um die Verwendung der selbstdefinierten Typen in Queries zu zeigen.

Neben den bereits vorgestellten Interfaces gibt es noch weitere, mit denen speziellere Mapping-Typen implementiert werden können.

Das Interface org.hibernate.usertype.ParameterizedType erlaubt es, Parameter über die Metadaten zu setzen, so kann z. B. ein Default-Wert bereits innerhalb der Metadaten fest-gelegt werden. org.hibernate.usertype.EnhancedUserType erlaubt die einfache Umwand-lung von User Types in XML und zurück. Das Interface org.hibernate.usertype.User-CollectionType ermöglicht die Implementierung einer benutzerdefinierten PersistentCollection. Mit org.hibernate.usertype.UserVersionType kann ein User Type implementiert werden, der als Versionsattribut verwendet werden kann.

Query query = em.createQuery("select u from UserWithComplexEmail u where dn = 'muster.de'" ); List<UserWithComplexEmail> list = query.getResultList(); for(UserWithComplexEmail user : list) { System.out.println(user); }

Listing 9.5: Query mit Attributen des UserType

Page 209: Jpa Mit en

9 – Hibernate Types

208

9.3 ZusammenfassungDa mit Hibernate für JPA bereits standardmäßig eine große Auswahl an Mapping-Typen zur Verfügung steht, ist es nicht zwingend erforderlich, eigene Mapping-Typen zu defi-nieren. Die Verwendung von Komponenten ist oft sogar sinnvoller als die Definition eines neuen User Types. Bei Typen, die von mehr als einer Entity verwendet werden und innerhalb der Gesamtanwendung oder darüber hinaus häufiger auftauchen, ist die Defi-nition eines eigenen User Types aber auf jeden Fall sinnvoll.

Page 210: Jpa Mit en

JPA mit Hibernate 209

AReferenz der Annotationen

A.1 Metadata-Annotationen

A.1.1 Entity

Signatur: @Entity

Paket: javax.persistence

Ziel: Entity

Beschreibung: Die Annotation Entity beschreibt eine Klasse als eine persistierbare Enti-tät. Sie kann nur auf Klassen angewandt werden.

Parameter:

XML-Äquivalent: entity Subelement von entity-mappings

Name Typ Beschreibung

name Zeichenkette Gibt den Namen der Entität an, wenn nicht der Name der Klasse verwendet werden soll

Page 211: Jpa Mit en

A – Referenz der Annotationen

210

A.2 Callback-Annotationen

A.2.1 EntityListeners

Signatur: @EntityListeners

Paket: javax.persistence

Ziel: Entity oder eine „abgebildete“ Superklasse

Beschreibung: Spezifiziert eine Liste von EntityListener-Implementierungen, die an der Entity registriert werden sollen.

Parameter:

XML-Äquivalent: entity-listener Subelement von entity

A.2.2 ExcludeSuperclassListeners

Signatur: @ExcludeSuperclassListeners

Paket: javax.persistence

Ziel: Entity oder „abgebildete“ Superklasse

Parameter: keine

XML-Äquivalent: exclude-superclass-listeners Subelement von entity

A.2.3 ExcludeDefaultListeners

Signatur: @ExcludeDefaultListeners

Paket: javax.persistence

Ziel: Entity oder „abgebildete“ Superklasse

Parameter: keine

XML-Äquivalent: exclude-default-listeners Subelement von entity

Name Typ Beschreibung

value Liste von Klassennamen Definiert eine Liste von EntityListener-Implementierungen

Page 212: Jpa Mit en

Callback-Annotationen

JPA mit Hibernate 211

A.2.4 PrePersist

Signatur: @PrePersist

Paket: javax.persistence

Ziel: Methode

Beschreibung: Signalisiert, dass die Methode aufgerufen werden soll, bevor eine Entity persistiert wird.

Parameter: keine

XML-Äquivalent: pre-persist Subelement von entity

A.2.5 PostPersist

Signatur: @PostPersist

Paket: javax.persistence

Ziel: Methode

Beschreibung: Signalisiert, dass die Methode aufgerufen werden soll, nachdem eine Entity persistiert wurde.

Parameter: keine

XML-Äquivalent: post-persist Subelement von entity

A.2.6 PreRemove

Signatur: @PreRemove

Paket: javax.persistence

Ziel: Methode

Beschreibung: Signalisiert, dass die Methode aufgerufen werden soll, bevor eine Entity gelöscht wird.

Parameter: keine

XML-Äquivalent: pre-remove Subelement von entity

Page 213: Jpa Mit en

A – Referenz der Annotationen

212

A.2.7 PostRemove

Signatur: @PostRemove

Paket: javax.persistence

Ziel: Methode

Beschreibung: Signalisiert, dass die Methode aufgerufen werden soll, nachdem eine Entity gelöscht wurde.

Parameter: keine

XML-Äquivalent: post-remove Subelement von entity

A.2.8 PreUpdate

Signatur: @PreUpdate

Paket: javax.persistence

Ziel: Methode

Beschreibung: Signalisiert, dass die Methode aufgerufen werden soll, bevor eine Entity aktualisiert wird.

Parameter: keine

XML-Äquivalent: pre-update Subelement von entity

A.2.9 PostUpdate

Signatur: @PostUpdate

Paket: javax.persistence

Ziel: Methode

Beschreibung: Signalisiert, dass die Methode aufgerufen werden soll, nachdem eine Entity aktualisiert wurde.

Parameter: keine

XML-Äquivalent: post-update Subelement von entity

A.2.10 PostLoad

Signatur: @PostLoad

Paket: javax.persistence

Ziel: Methode

Beschreibung: Signalisiert, dass die Methode aufgerufen werden soll, nachdem eine Entity geladen wurde.

Parameter: keine

XML-Äquivalent: post-load Subelement von entity

Page 214: Jpa Mit en

Annotationen für Datenbankabfragen

JPA mit Hibernate 213

A.3 Annotationen für Datenbankabfragen

A.3.1 NamedQuery

Signatur: @NamedQuery

Paket: javax.persistence

Ziel: Entity oder eine „abgebildete“ Superklasse

Beschreibung: Definiert eine benannte Datenbankabfrage in der Java Persistence Query Language.

Parameter:

XML-Äquivalent: named-query Subelement von entity

A.3.2 QueryHint

Signatur: @QueryHint

Paket: javax.persistence

Ziel: Entity oder eine „abgebildete“ Superklasse

Beschreibung: Definiert einen Abfrageparameter.

Parameter:

A.3.3 NamedQueries

Signatur: @NamedQueries

Paket: javax.persistence

Ziel: Entity oder eine „abgebildete“ Superklasse

Beschreibung: Definiert eine Liste von NamedQueries.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name der Datenbankabfrage

query Zeichenkette Die Syntax der Datenbankabfrage

hints QueryHint[ ] Eine Liste von vordefinierten Parametern (mit der Annotation @QueryHint)

Name Typ Beschreibung

name Zeichenkette Der Name des Parameters

value Zeichenkette Der Wert des Parameters

Name Typ Beschreibung

value NamedQuery[ ] Eine Liste von NamedQueries, die mit Annotation @NamedQuery definiert wurde

Page 215: Jpa Mit en

A – Referenz der Annotationen

214

A.3.4 NamedNativeQuery

Signatur: @NamedNativeQuery

Paket: javax.persistence

Ziel: Entity oder eine „abgebildete“ Superklasse

Beschreibung: Definiert eine benannte Datenbankabfrage mithilfe der SQL-Syntax.

Parameter:

XML-Äquivalent: named-native-query Subelement von entity

A.3.5 NamedNativeQueries

Signatur: @NamedNativeQueries

Paket: javax.persistence

Ziel: Entity oder eine „abgebildete“ Superklasse

Beschreibung: Definiert eine Liste von NamedNativeQueries.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name der Datenbankabfrage

query Zeichenkette Die Syntax der Datenbankabfrage

hints QueryHint[ ] Eine Liste von vordefinierten Parametern (mit Annotation @QueryHint)

resultClass Class Definiert den Typ des Abfrageergebnisses als Klasse

resultSetMapping Zeichenkette Der Name eines SqlResultSetMappings

Name Typ Beschreibung

value NamedNativeQuery[ ] Eine Liste von NamedNativeQueries, die mit der Annotation @NamedNativeQuery definiert wurden

Page 216: Jpa Mit en

Abbilden der SQL-Abfrageergebnisse

JPA mit Hibernate 215

A.4 Abbilden der SQL-Abfrageergebnisse

A.4.1 SQLResultSetMapping

Signatur: @SQLResultSetMapping

Paket: javax.persistence

Ziel: Entity oder eine „abgebildete“ Superklasse

Beschreibung: Definiert ein Mapping zwischen dem Abfrageergebnis und den Objekten bzw. skalaren Werten.

Parameter:

XML-Äquivalent: sql-result-set-mapping Subelement von entity

A.4.2 SQLResultSetMappings

Signatur: @SQLResultSetMappings

Paket: javax.persistence

Ziel: Entity oder eine „abgebildete“ Superklasse

Beschreibung: Legt eine Liste von SQLResultSetMappings an.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name des Resultset-Mappings

entities EntityResult[ ] Eine Liste von EntityResult-Definitionen

columns ColumnResult[ ] Eine Liste von ColumResult-Definitionen

Name Typ Beschreibung

value SQLResultSetMapping[ ] Eine Liste von SQLResultSetMappings, die mit der Annota-tion @SQLResultSetMapping definiert wurden

Page 217: Jpa Mit en

A – Referenz der Annotationen

216

A.4.3 EntityResult

Signatur: @EntityResult

Paket: javax.persistence

Ziel: Entity oder eine „abgebildete“ Superklasse

Beschreibung: Definiert ein Mapping zwischen dem Abfrageergebnis und einer Entität.

Parameter:

A.4.4 FieldResult

Signatur: @FieldResult

Paket: javax.persistence

Ziel: Entity oder eine „abgebildete“ Superklasse

Beschreibung: Diese Annotation wird benutzt, um Spalten aus einer Ergebnismenge den Feldern in einer Entity zuzuordnen.

Parameter:

A.4.5 ColumnResult

Signatur: @ColumnResult

Paket: javax.persistence

Ziel: Entity oder eine „abgebildete“ Superklasse

Beschreibung: Definiert ein Mapping zwischen einem Spaltennamen und einem skala-ren Wert.

Parameter:

Name Typ Beschreibung

entityClass Class Die Entity des Ergebnisses

fields FieldResult[ ] Eine Liste über die Feld-Mapping-Definitionen (@FieldResult)

discriminatorColumn Zeichenkette Gibt den Namen der Spalte in der Ergebnismenge an, die den Typ der Entity bestimmt

Name Typ Beschreibung

name Zeichenkette Der Name des persistenten Feldes in der Entity

Name Typ Beschreibung

name Zeichenkette Der Name der Spalte innerhalb der Ergebnismenge

Page 218: Jpa Mit en

Referenzen auf den EntityManager und die EntityManagerFactory

JPA mit Hibernate 217

A.5 Referenzen auf den EntityManager und die EntityManagerFactory

A.5.1 PersistenceContext

Signatur: @PersistenceContext

Paket: javax.persistence

Ziel: Klasse, Methode oder Feld

Beschreibung: Legt die Abhängigkeit der Methode, des Felds oder der Klasse zu einem PersistenceContext des EntityManagers fest.

Parameter:

A.5.2 PersistenceProperty

Signatur: @PersistenceProperty

Paket: javax.persistence

Ziel: Klasse, Methode oder Feld

Beschreibung: Definiert eine herstellerabhängige Eigenschaft für eine Referenz auf den PersistenceContext.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name des EntityManagers

unitName Zeichenkette Name der persistierten Einheit

type PersistenceContextType Gibt an, welche Art des PersistenceContexts benutzt werden soll. Mögliche Werte sind TRANSACTION oder EXTENDED.

properties PersistenceProperty[ ] Eine Liste von Eigenschaften

Name Typ Beschreibung

name Zeichenkette Der Name der Eigenschaft

value Zeichenkette Der Wert, der der Eigenschaft zugewiesen werden soll

Page 219: Jpa Mit en

A – Referenz der Annotationen

218

A.5.3 PersistenceContexts

Signatur: @PersistenceContexts

Paket: javax.persistence

Ziel: Klasse

Beschreibung: Definiert eine Liste von PersistenceContext-Annotationen.

Parameter:

A.5.4 PersistenceUnit

Signatur: @PersistenceUnit

Paket: javax.persistence

Ziel: Klasse, Methode oder Feld

Beschreibung: Legt eine Abhängigkeit zu einer bestimmten EntityManagerFactory fest.

Parameter:

A.5.5 PersistenceUnits

Signatur: @PersistentUnits

Paket: javax.persistence

Ziel: Klasse

Beschreibung: Definiert eine Liste von PersistenceUnit-Annotationen.

Parameter:

Name Typ Beschreibung

value PersistenceContext[ ] Eine Liste von PersistenceContext-Annotationen

Name Typ Beschreibung

name Zeichenkette Der Name der EntityManagerFactory

unitName Zeichenkette Der Name der persistenten Einheit

Name Typ Beschreibung

value PersistenceUnit[ ] Eine Liste von PersistenceUnit-Annotationen

Page 220: Jpa Mit en

Annotationen für die Definition der Abbildungen der Entitäten

JPA mit Hibernate 219

A.6 Annotationen für die Definition der Abbildungen der Entitäten

A.6.1 Table

Signatur: @Table

Paket: javax.persistence

Ziel: Entity

Beschreibung: Definiert die primäre Datenbanktabelle für die Entity.

Parameter:

XML-Äquivalent: table Subelement von entity

A.6.2 UniqueConstraint

Signatur: @UniqueConstraint

Paket: javax.persistence

Ziel: Entity

Beschreibung: Definiert eine Menge von Spalten, denen das Eindeutigkeitsmerkmal zugewiesen wird.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name der Tabelle

catalog Zeichenkette Der Tabellenkatalog

schema Zeichenkette Der Name des Schemas

uniqueConstraints UniqueConstraint[ ] Liste aller „unique constraints“, um Spalten mit einem Eindeutigkeitsmerkmal zu versehen

Name Typ Beschreibung

columnNames Zeichenkette[ ] Liste der Spaltennamen

Page 221: Jpa Mit en

A – Referenz der Annotationen

220

A.6.3 SecondaryTable

Signatur: @SecondaryTable

Paket: javax.persistence

Ziel: Entity

Beschreibung: Definiert eine weitere Datenbanktabelle für die Entity.

Parameter:

XML-Äquivalent: secondary-table Subelement von entity

A.6.4 SecondaryTables

Signatur: @SecondaryTables

Paket: javax.persistence

Ziel: Entity

Beschreibung: Definiert eine Liste von SecondaryTable-Annotationen.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name der Tabelle

catalog Zeichenkette Der Tabellenkatalog

schema Zeichenkette Der Name des Schemas

pkJoinColumns PrimaryKeyJoinColumn[ ] Eine Liste von Spalten, die zum Verknüpfen von Tabellen verwendet werden

uniqueConstraints UniqueConstraint[ ] Liste aller „unique constraints“, um Spalten mit einem Eindeutigkeitsmerkmal zu versehen

Name Typ Beschreibung

value SecondaryTable[ ] Eine Liste von SecondaryTable-Annotationen

Page 222: Jpa Mit en

Annotationen für die Definition der Abbildungen der Entitäten

JPA mit Hibernate 221

A.6.5 CollectionTable

Signatur: @CollectionTable

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Neu in JPA 2.0. Definiert die Datenbanktabelle, in der die Werte der Coll-ection von Basistypen oder Komponenten (Embeddables) gespeichert werden (siehe auch @ElementCollection).

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name der Tabelle

catalog Zeichenkette Der Tabellenkatalog

schema Zeichenkette Der Name des Schemas

joinColumns JoinColumn[ ] Legt die Spalte für den Fremdschlüssel in der Collection-Tabelle auf die Entity fest.

uniqueConstraints UniqueConstraint[ ] Liste aller „unique constraints“, um Spalten mit einem Eindeutigkeitsmerkmal zu versehen

Page 223: Jpa Mit en

A – Referenz der Annotationen

222

A.7 Definieren von Primärschlüsseln

A.7.1 Id

Signatur: @Id

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung:

Spezifiziert das Feld oder die Methode als Primärschlüsselfeld.

Parameter: keine

XML-Äquivalent: id Subelement von attributes

A.7.2 GeneratedValue

Signatur: @GeneratedValue

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung:

Legt die Strategie fest, mit der neue Primärschlüssel generiert werden.

Parameter:

A.7.3 EmbeddedId

Signatur: @EmbeddedId

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung:Definiert eine eingebettete Klasse als zusammengesetzten Primärschlüssel für die aktuelle Entität. Siehe auch @Embeddable.

Parameter: keine

XML-Äquivalent: embedded-id Subelement von attributes

Name Typ Beschreibung

generator Zeichenkette Der Name des Primärschlüsselgenerators

strategy GenerationType Die Generatorstrategie. Folgende Werte sind möglich:� GenerationType.AUTO� GenerationType.TABLE� GenerationType.SEQUENCE� GenerationType.IDENTITY

Page 224: Jpa Mit en

Definieren von Primärschlüsseln

JPA mit Hibernate 223

A.7.4 IdClass

Signatur: @IdClass

Paket: javax.persistence

Ziel: Entity oder abgebildete Superklasse

Beschreibung: Definiert eine Klasse als zusammengesetzten Primärschlüssel, dessen Felder auf mehrere Felder der aktuellen Entität abgebildet werden.

Parameter:

XML-Äquivalent: id-class Subelement von attributes

A.7.5 SequenceGenerator

Signatur: @SequenceGenerator

Paket: javax.persistence

Ziel: Klasse, Methode oder Feld

Beschreibung: Definiert einen Primärschlüsselgenerator, der eine laufende Nummer ver-waltet.

Parameter:

XML-Äquivalent: sequence-generator Subelement von attributes

Name Typ Beschreibung

value Class Das Klassenobjekt der Primärschlüsselklasse

Name Typ Beschreibung

name Zeichenkette Der Name des Primärschlüsselgenerators

sequenceName Zeichenkette Der Name der zu verwendenden Sequence innerhalb der Daten-bank

initialValue Nummer Legt die Nummer fest, mit der die Sequence starten soll

allocationSize Nummer Legt fest, wie viele Sequence-Nummern reserviert werden sollen

Page 225: Jpa Mit en

A – Referenz der Annotationen

224

A.7.6 TableGenerator

Signatur: @TableGenerator

Paket: javax.persistence

Ziel: Klasse, Methode oder Feld

Beschreibung: Spezifiziert einen Primärschlüsselgenerator mithilfe einer Tabelle.

Parameter:

XML-Äquivalent: table-generator Subelement von attributes

A.7.7 MapsId

Signatur: @MapsId

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Neu in JPA 2.0. Spezifiziert für eine n-zu-1- oder 1-zu-1-Beziehung einen abgeleiteten Primärschlüssel.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name des Primärschlüsselgenerators

table Zeichenkette Der Name der Tabelle, die die generierten Primärschlüssel speichert

catalog Zeichenkette Der Datenbankkatalog, in dem sich die Tabelle befindet

schema Zeichenkette Das Datenbankschema der Tabelle

pkColumnName Zeichenkette Der Name der Primärschlüsselspalte

valueColumnName Zeichenkette Der Name der Spalte mit dem letzten generierten Wert

pkColumnValue Zeichenkette Der Name des Werts des Primärschlüssels, mit dem zwischen den einzelnen generierten Werten für die aktuelle Entität unterschieden wird

initialValue Nummer Legt die Nummer fest, mit der die Sequence starten soll

allocationSize Nummer Legt fest, wie viele Sequence-Nummern reserviert werden sollen

uniqueConstraints Unique-Constraint[ ]

Liste aller „unique constraints“, um Spalten mit einem Eindeutigkeitsmerkmal zu versehen

Name Typ Beschreibung

value Zeichenkette Name des Attributs, auf das sich der Primärschlüssel bezieht.

Page 226: Jpa Mit en

Annotationen zum Überschreiben bestehender Abbildungen

JPA mit Hibernate 225

A.8 Annotationen zum Überschreiben bestehender Abbildungen

A.8.1 AttributeOverride

Signatur: @AttributeOverride

Paket: javax.persistence

Ziel: Klasse, Methode oder Feld

Beschreibung: Überschreibt die Definition eines Felds in der Superklasse.

Parameter:

XML-Äquivalent: attribute-override Subelement von attributes

A.8.2 AttributeOverrides

Signatur: @AttributeOverrides

Paket: javax.persistence

Ziel: Klasse, Methode oder Feld

Beschreibung: Definiert eine Menge von @AttributeOverrides-Annotationen.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name des Felds oder der ID

column Column Eine @Column-Definition

Name Typ Beschreibung

value AttributeOverride[ ] Eine Liste von AttributeOverrides

Page 227: Jpa Mit en

A – Referenz der Annotationen

226

A.8.3 AssociationOverride

Signatur: @AssociationOverride

Paket: javax.persistence

Ziel: Klasse, Methode oder Feld

Beschreibung:

Überschreibt die Definition einer Assoziation in der Superklasse.

Parameter:

XML-Äquivalent: association-override Subelement von attributes

A.8.4 AssociationOverrides

Signatur: @AssociationOverrides

Paket: javax.persistence

Ziel: Klasse, Methode oder Feld

Beschreibung: Definiert eine Menge von @AssociationOverrides-Annotationen.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name der Assoziation

joinColumns JoinColumn[ ] Eine Liste von @JoinColumn Definitionen

Name Typ Beschreibung

value AssociationOverride[ ] Eine Liste von AssociationOverrides

Page 228: Jpa Mit en

Annotationen für Entitätseigenschaften

JPA mit Hibernate 227

A.9 Annotationen für Entitätseigenschaften

A.9.1 Transient

Signatur: @Transient

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Spezifiziert ein nicht persistentes Feld in der Entität.

Parameter: keine

XML-Äquivalent: transient Subelement von attributes

A.9.2 Column

Signatur: @Column

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Definiert die Spalte für die Property der Entität.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name der Spalte

unique true/false Ist die Eigenschaft unique?

nullable true/false Darf der Wert null werden?

insertable true/false Soll die Spalte in SQL INSERTs aufgenommen werden?

updatable true/false Soll die Spalte in SQL UPDATEs aufgenommen werden?

columnDefinition Zeichenkette SQL-Fragment für die DDL-Script-Generierung

table Zeichenkette Name der Tabelle, in der die Spalte angeben ist

length numerisch Länge der Spalte

precision numerisch Genauigkeit von Dezimalzahlen

scale numerisch Skalierung von Dezimalzahlen

Page 229: Jpa Mit en

A – Referenz der Annotationen

228

A.9.3 Basic

Signatur: @Basic

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Definiert eine Abbildung eines Felds mit folgenden Datentypen:

� byte, char, short, int, long, float, double und deren Wrapper-Typen� java.lang.String, java.math.BigInteger,

� java.math.BigDecimal,

� java.util.Date,

� java.util.Calendar,

� java.sql.Date,

� java.sql.Time,

� java.sql.Timestamp,

� byte[] und Byte[],

� char[] und Character[]

� Enumerationen und Ableitungen von Serializable

Parameter:

XML-Äquivalent: basic Subelement von attributes

A.9.4 Lob

Signatur: @Lob

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Spezifiziert das Feld als Lob-Typ. Ein Lob-Typ kann entweder binär oder zeichenorientiert sein. Dies wird anhand des Feldtyps bestimmt. Standardmäßig wird ein BLOB verwendet, Strings und character-based Typen werden mit CLOB abgebildet.

Parameter: keine

Name Typ Beschreibung

fetch FetchType Der FetchType des Felds. Folgende Werte sind erlaubt:� FetchType.LAZY� FetchType.EAGER

optional true/false Ob der Wert des Feldes null sein darf

Page 230: Jpa Mit en

Annotationen für Entitätseigenschaften

JPA mit Hibernate 229

A.9.5 Temporal

Signatur: @Temporal

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Bestimmt das Feld als Datums- oder Zeittyp. Es darf nur bei java.util.date oder java.util.Calendar angewandt werden.

Parameter:

A.9.6 Enumerated

Signatur: @Enumerated

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Verwendet ein Feld einen Enumerationstyp, muss mithilfe dieser Anno-tation die Abbildung festgelegt werden.

Parameter:

A.9.7 Version

Signatur: @Version

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Spezifiziert das zur Versionierung verwendete Feld der Entität.

Parameter: keine

XML-Äquivalent: version Subelement von attributes

Name Typ Beschreibung

value TemporalType Der Typ, der bestimmt, wie ein temporales Feld abgebildet werden soll. Mögliche Werte sind:� TemporalType.DATE� TemporalType.TIME� TemporalType.TIMESTAMP

Name Typ Beschreibung

value EnumType Legt fest, wie das Feld abgebildet wird. Mögliche Werte sind:� EnumType.ORDINAL� EnumType.STRING

Page 231: Jpa Mit en

A – Referenz der Annotationen

230

A.9.8 Access

Signatur: @Access

Paket: javax.persistence

Ziel: Entity, Methode oder Feld

Beschreibung: Neu in JPA 2.0. Spezifiziert den Zugriffstyp auf Werte der Entity.

Parameter:

XML-Äquivalent: access Subelement von attributes

A.9.9 Cacheable

Signatur: @Cacheable

Paket: javax.persistence

Ziel: Entity

Beschreibung: Neu in JPA 2.0. Definiert, wie die Entity in Bezug auf einen Second Level Cache behandelt wird.

Parameter:

XML-Äquivalent: cacheable Subelement von entity

Name Typ Beschreibung

value AccessType FIELD für Zugriff via Instanzvariable; PROPERTY für Zugriff via Getter Methode

Name Typ Beschreibung

value boolean Legt fest, ob die Entity gecacht werden darf oder nicht.

Page 232: Jpa Mit en

Annotationen für Assoziationen

JPA mit Hibernate 231

A.10 Annotationen für Assoziationen

A.10.1 JoinColumn

Signatur: @JoinColumn

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Definiert eine Tabellenspalte als Fremdschlüssel der Assoziation.

Parameter:

A.10.2 JoinColumns

Signatur: @JoinColumns

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Definiert eine Liste von @JoinColumn-Annotationen.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name der Fremdschlüsselspalte

referencedColumnName Zeichenkette Der Name der Spalte, auf die sich der Fremdschlüssel in der Tabelle der aktuellen Entität bezieht

unique true/false Ist die Eigenschaft unique

nullable true/false Darf der Wert null werden

insertable true/false Soll die Spalte in SQL INSERTs aufgenommen werden

updatable true/false Soll die Spalte in SQL UPDATEs aufgenommen werden

columnDefinition Zeichenkette SQL-Fragment für die DDL-Script-Generierung

table Zeichenkette Name der Tabelle, in der die Spalte angegeben ist

Name Typ Beschreibung

value JoinColumn[ ] Eine Liste von @JoinColumn Annotationen

Page 233: Jpa Mit en

A – Referenz der Annotationen

232

A.10.3 ManyToOne

Signatur: @ManyToOne

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Legt fest, dass dieses Feld eine Assoziation vom Typ „Viele-zu-Eins“ ist.

Parameter:

XML-Äquivalent: many-to-one Subelement von attributes

A.10.4 OneToOne

Signatur: @OneToOne

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Legt fest, dass dieses Feld eine Assoziation vom Typ „Eins-zu-Eins“ ist.

Parameter:

XML-Äquivalent: one-to-one Subelement von attributes

Name Typ Beschreibung

targetEntity Class Die Klasse der Entität, die das Ziel der Assoziation darstellt

cascade CascadeType[] Definiert die Kaskadierungsstrategie. Mögliche Werte sind:

� CascadeType.ALL� CascadeType.PERSIST� CascadeType.MERGE

� CascadeType.REMOVE� CascadeType.REFRESH

fetch FetchType Der FetchType des Felds. Folgende Werte sind erlaubt:

� FetchType.LAZY � FetchType.EAGER

optional true/false Spezifiziert, ob die Assoziation optional ist. D.h., dass bei optional=false immer eine Beziehung zu einer existierenden Entität bestehen muss.

Name Typ Beschreibung

targetEntity Class Die Klasse der Entität, die das Ziel der Assoziation darstellt

cascade CascadeType[ ] Definiert die Kaskadierungsstrategie. Mögliche Werte sind:

� CascadeType.ALL� CascadeType.PERSIST� CascadeType.MERGE

� CascadeType.REMOVE� CascadeType.REFRESH

fetch FetchType Der FetchType des Felds. Folgende Werte sind erlaubt:

� FetchType.LAZY � FetchType.EAGER

optional true/false Spezifiziert, ob die Assoziation optional ist. D.h., dass bei optional=false immer eine Beziehung zu einer existierenden Entität bestehen muss.

mappedBy Zeichenkette Legt den Namen des Felds fest, das die Beziehung realisiert. MappedBy wird nur bei der inversen Seite der Assoziation angegeben.

Page 234: Jpa Mit en

Annotationen für Assoziationen

JPA mit Hibernate 233

A.10.5 OneToMany

Signatur: @OneToMany

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Legt fest, dass dieses Feld eine Assoziation vom Typ „Eins-zu-Viele“ ist.

Parameter:

XML-Äquivalent: one-to-many Subelement von attributes

A.10.6 ManyToMany

Signatur: @ManyToMany

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Legt fest, dass dieses Feld eine Assoziation vom Typ „Viele-zu-Viele“ ist.

Parameter:

XML-Äquivalent: many-to-many Subelement von attributes

Name Typ Beschreibung

targetEntity Class Die Klasse der Entität, die das Ziel der Assoziation darstellt

cascade CascadeType[ ] Definiert die Kaskadierungsstrategie. Mögliche Werte sind:

� CascadeType.ALL� CascadeType.PERSIST� CascadeType.MERGE

� CascadeType.REMOVE� CascadeType.REFRESH

fetch FetchType Der FetchType des Felds. Folgende Werte sind erlaubt:

� FetchType.LAZY � FetchType.EAGER

mappedBy Zeichenkette Legt den Namen des Felds fest, welches die Beziehung realisiert. MappedBy wird nur bei der inversen Seite der Assoziation angegeben.

Name Typ Beschreibung

targetEntity Class Die Klasse der Entität, die das Ziel der Assoziation darstellt

cascade CascadeType[ ] Definiert die Kaskadierungsstrategie. Mögliche Werte sind:

� CascadeType.ALL� CascadeType.PERSIST� CascadeType. MERGE

� CascadeType.REMOVE� CascadeType.REFRESH

fetch FetchType Der FetchType des Felds. Folgende Werte sind erlaubt:

� FetchType.LAZY � FetchType.EAGER

mappedBy Zeichenkette Legt den Namen des Felds fest, das die Beziehung realisiert. MappedBy wird nur bei der inversen Seite der Assoziation angegeben.

Page 235: Jpa Mit en

A – Referenz der Annotationen

234

A.10.7 JoinTable

Signatur: @JoinTable

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Spezifiziert die Abbildung einer Assoziation mithilfe einer Join-Tabelle.

Parameter:

A.10.8 MapKey

Signatur: @MapKey

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Diese Annotation wird verwendet, um einen „Map-Schlüssel“ für Asso-ziationen vom Typ java.util.Map zu definieren.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name der Tabelle

catalog Zeichenkette Der Tabellenkatalog

schema Zeichenkette Der Name des Schemas

joinColumns JoinColumn[ ] Eine Liste von Definitionen für Spalten mit Fremdschlüssel auf die Tabelle der realisierenden Seite der Assoziation (siehe @JoinColumn).

inverseJoinColumns JoinColumn[ ] Eine Liste von Definitionen für Spalten mit Fremdschlüssel auf die Tabelle der nicht realisierenden Seite der Assozia-tion (siehe @JoinColumn).

uniqueConstraints UniqueConstraint[ ] Liste aller „unique constraints“, um Spalten mit einem Ein-deutigkeitsmerkmal zu versehen

Name Typ Beschreibung

name Zeichenkette Der Name des Felds der assoziierten Entität, das als Map-Schlüssel verwendet werden soll

Page 236: Jpa Mit en

Annotationen für Assoziationen

JPA mit Hibernate 235

A.10.9 MapKeyClass

Signatur: @MapKeyClass

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Neu in JPA 2.0. Diese Annotation wird verwendet, um den Typ des „Map-Schlüssels“ zu bestimmen. Als Schlüssel einer Map können Basistypen, Embed-dables und Entites verwendet werden.

Parameter:

A.10.10 MapKeyColumn

Signatur: @MapKeyColumn

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Neu in JPA 2.0. Definiert die Tabellenspalte für den Schlüssel einer Map, wenn dieser ein Basistyp ist.

Parameter:

Name Typ Beschreibung

value Class Typ des Map-Schlüssels

Name Typ Beschreibung

name Zeichenkette Der Name der Spalte

length Zahl Länge der Spalte, wird nur ausgewertet, wenn Schlüssel vom Typ String ist

unique true/false Ist die Eigenschaft unique

nullable true/false Darf der Wert null werden

insertable true/false Soll die Spalte in SQL INSERTs aufgenommen werden

updatable true/false Soll die Spalte in SQL UPDATEs aufgenommen werden

columnDefinition Zeichenkette SQL-Fragment für die DDL-Script-Generierung

table Zeichenkette Name der Tabelle, in der die Spalte angeben ist

Page 237: Jpa Mit en

A – Referenz der Annotationen

236

A.10.11 MapKeyEnumerated

Signatur: @MapKeyEnumerated

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Neu in JPA 2.0. Diese Annotation wird verwendet, um eine Enumeration als „Map-Schlüssel“ zu verwenden.

Parameter:

A.10.12 MapKeyJoinColumn

Signatur: @MapKeyJoinColumn

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung:

Neu in JPA 2.0. Spezifiziert die Datenbankspalte für die Beziehung zu einer Entity, die als Schlüssel in einer Map verwendet wird.

Parameter:

Name Typ Beschreibung

value EnumType Mapping Type der Enumeration (Default: ORDINAL)

Name Typ Beschreibung

name Zeichenkette Der Name der Spalte

referencedColumnName Zeichenkette Der Name der Spalte, auf die sich der Fremdschlüssel in der Tabelle der aktuellen Entity bezieht

unique true/false Ist die Eigenschaft unique?

nullable true/false Darf der Wert null werden?

insertable true/false Soll die Spalte in SQL INSERTs aufgenommen werden?

updatable true/false Soll die Spalte in SQL UPDATEs aufgenommen werden?

columnDefinition Zeichenkette SQL-Fragment für die DDL-Script-Generierung

table Zeichenkette Name der Tabelle, in der die Spalte angeben ist

Page 238: Jpa Mit en

Annotationen für Assoziationen

JPA mit Hibernate 237

A.10.13 MapKeyJoinColumns

Signatur: @MapKeyJoinColumns

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Neu in JPA 2.0. Definiert eine Liste von @MapKeyJoinColumn-Annotati-onen.

Parameter:

A.10.14 MapKeyTemporal

Signatur: @MapKeyTemporal

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Neu in JPA 2.0. Diese Annotation wird verwendet, um einen Zeit-Daten-typ als „Map-Schlüssel“ zu verwenden.

Parameter:

A.10.15 OrderBy

Signatur: @OrderBy

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Spezifiziert die Sortierungsreihenfolge der Assoziation.

Parameter:

Name Typ Beschreibung

value MapKeyJoinColumn[ ] Eine Liste von @ MapKeyJoinColumn-Annotationen

Name Typ Beschreibung

value TemporalType Der Java-Datentyp für den Schlüssel. Entweder java.util.Date oder java.util.Calendar.

Name Typ Beschreibung

value Zeichenkette Die Syntax des value-Parameters ist:list::=item[,item]*item::=feldname [ASC|DESC]

Page 239: Jpa Mit en

A – Referenz der Annotationen

238

A.10.16 OrderColumn

Signatur: @OrderColumn

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Neu in JPA 2.0. Die Annotation spezifiziert eine Datenbankspalte, die die Reihenfolge der Elemente einer Collection persisitert. Der Persistence Provider ist für das korrekte Speichern und Aktualisieren verantwortlich. Die Datenbankspalte ist nicht als Attribut für die Entity sichtbar.

Parameter:

A.10.17 PrimaryKeyJoinColumn

Signatur: @PrimaryKeyJoinColumn

Paket: javax.persistence

Ziel: Klasse, Methode oder Feld

Beschreibung: Spezifiziert eine Primärschlüsselspalte, die innerhalb der Join-Verbin-dung zu einer anderen Tabelle als Fremdschlüssel verwendet werden soll.

Parameter:

XML-Äquivalent: primary-key-join-column Subelement von attributes

Name Typ Beschreibung

name Zeichenkette Der Name der Fremdschlüsselspalte

nullable true/false Darf der Wert null werden

insertable true/false Soll die Spalte in SQL INSERTs aufgenommen werden

updatable true/false Soll die Spalte in SQL UPDATEs aufgenommen werden

columnDefinition Zeichenkette SQL Fragement für die DDL-Script-Generierung

Name Typ Beschreibung

name Zeichenkette Der Name der Primärschlüsselspalte

referencedColumnName Zeichenkette Der Name der Fremdschlüsselspalte, auf die sich der Primärschlüssel bezieht

columnDefinition Zeichenkette SQL-Fragment für die DDL-Script-Generierung

Page 240: Jpa Mit en

Annotationen für Assoziationen

JPA mit Hibernate 239

A.10.18 PrimaryKeyJoinColumns

Signatur: @PrimaryKeyJoinColumns

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Definiert eine Liste von @PrimaryKeyJoinColumns.

Parameter:

A.10.19 ElementCollection

Signatur: @ElementCollection

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Neu in JPA 2.0. Definiert eine Collection von Basistypen oder Komponen-ten (Embeddables).

Parameter:

XML-Äquivalent: element-collection Subelement von attributes

Name Typ Beschreibung

value PrimaryKeyJoinColumn[ ] Eine Liste von @PrimaryKeyJoinColumn-Definitionen

Name Typ Beschreibung

targetClass Class Typ der Collection, falls dieser nicht durch Verwendung von Generics bestimmt ist

fetch FetchType Der FetchType des Felds. Folgende Werte sind erlaubt:� FetchType.LAZY� FetchType.EAGER

Page 241: Jpa Mit en

A – Referenz der Annotationen

240

A.11 Annotationen für Vererbung

A.11.1 Inheritance

Signatur: @Inheritance

Paket: javax.persistence

Ziel: Entity

Beschreibung: Definiert die Strategie, wie die Vererbungshierarchie abgebildet werden soll.

Parameter:

XML-Äquivalent: inheritance Subelement von attributes

A.11.2 DiscriminatorColumn

Signatur: @DiscriminatorColumn

Paket: javax.persistence

Ziel: Die Oberklasse der Vererbungshierarchie

Beschreibung: Legt eine Spalte für die Unterscheidung der Subklassen fest.

Parameter:

XML-Äquivalent: discriminator-column Subelement von attributes

Name Typ Beschreibung

strategy InheritanceType Die Abbildungsstrategie für Vererbungshierarchien. Mögliche Werte sind:� InheritanceType.SINGLE_TABLE� InheritanceType.JOINED� InheritanceType.TABLE_PER_CLASS

Name Typ Beschreibung

name Zeichenkette Der Name der Spalte

discriminatorType DiscriminatorType Der Typ des Discriminators. Mögliche Werte sind:� DiscriminatorType.STRING� DiscriminatorType.CHAR� DiscriminatorType.INTEGER

columnDefinition Zeichenkette Das SQL-Fragment, mit dem die Spalte in einem DDL-Skript angelegt wird

length Zeichenkette Die Länge der Spalte

Page 242: Jpa Mit en

Annotationen für Vererbung

JPA mit Hibernate 241

A.11.3 DiscriminatorValue

Signatur: @DiscriminatorValue

Paket: javax.persistence

Ziel: Entity

Beschreibung: Legt den Wert der Discriminator-Spalte fest, der für Objekte des aktuellen Typs verwendet werden soll.

Parameter:

XML-Äquivalent: discriminator-value Subelement von attributes

A.11.4 MappedSuperclass

Signatur: @MappedSuperclass

Paket: javax.persistence

Ziel: Klasse

Beschreibung: Spezifiziert diese Klasse als eine Superklasse, von der Entitäten erben können. Eine MappedSuperclass besitzt keine eigene Tabelle. Die Felder der Superclass werden in den Tabellen der erbenden Entitäten abgebildet.

Parameter: keine

XML-Äquivalent: mapped-superclass Subelement von entity-mappings

Name Typ Beschreibung

value Zeichenkette Der Wert der Discriminator-Spalte

Page 243: Jpa Mit en

A – Referenz der Annotationen

242

A.12 Annotationen für eingebettete Komponenten

A.12.1 Embeddable

Signatur: @Embeddable

Paket: javax.persistence

Ziel: Klasse

Beschreibung: Legt eine Komponente fest, deren Felder in einer Entity eingebettet wer-den sollen. Die Komponente teilt die Identität mit der Entity.

Parameter: keine

XML-Äquivalent: embeddable Subelement von entity-mappings

A.12.2 Embedded

Signatur: @Embedded

Paket: javax.persistence

Ziel: Methode oder Feld

Beschreibung: Legt fest, dass das aktuelle Feld eine Referenz auf eine eingebettete Kom-ponente der Entität darstellt.

Parameter: keine

XML-Äquivalent: embeddable Subelement von attributes

Page 244: Jpa Mit en

Hibernate-spezifische Annotationen

JPA mit Hibernate 243

A.13 Hibernate-spezifische Annotationen

A.13.1 Entity

Signatur: @Entity

Paket: org.hibernate.annotations

Ziel: Entity

Beschreibung: Erweitert die javax.persistence.Entity-Annotation um Hibernate-spezi-fische Einstellungen.

Parameter:

Name Typ Beschreibung

dynamicInsert true/false In SQL-Insert-Anweisungen werden nur die tatsächlich benötigten Spalten verwendet

dynamicUpdate true/false In SQL-Update-Anweisungen werden nur die tatsächlich benötigten Spalten verwendet

mutable true/false Legt fest, ob diese Entität unveränderlich (read only) oder nicht ist

optimisticLock OptimisticLockType Legt die Strategie für das optimistische Locking fest. Mögliche Werte sind:� OptimisticLockType.ALL, � OptimisticLockType.DIRTY, � OptimisticLockType.NONE, � OptimisticLockType.VERSION

persister Zeichenkette Benennt den zu verwendenden Persister für diese Entität

polymorphism PolymorphismType Legt die Strategie für den Polymorphismus fest. Mögliche Werte sind:� PolymorphismType.IMPLICIT, � PolymorphismType.EXPLICIT

selectBeforeUpdate true/false Legt fest, ob vor dem Update die Entität erneut geladen werden soll

Page 245: Jpa Mit en

A – Referenz der Annotationen

244

A.13.2 Table

Signatur: @Table

Paket: org.hibernate.annotations

Ziel: Entity

Beschreibung: Zusätzliche Einstellungen für eine primäre oder sekundäre Tabellendefi-nition.

Parameter:

A.13.3 Index

Signatur: @Index

Paket: org.hibernate.annotations

Ziel: Entity

Beschreibung: Definiert einen Datenbankindex.

Parameter:

A.13.4 Tables

Signatur: @Tables

Paket: org.hibernate.annotations

Ziel: Entity

Beschreibung: Definiert mehrere Tabellen.

Parameter:

Name Typ Beschreibung

appliesTo Zeichenkette Der Name der Tabelle, für die diese Einstellungen gelten

indexes Index[ ] Eine Liste von @Index-Annotationen

Name Typ Beschreibung

name Zeichenkette Der Name des Index

columnNames Zeichenkette[ ] Eine Liste von Spaltennamen

Name Typ Beschreibung

values Table[ ] Eine Liste von Tabellendefinitionen mit der @Table-Annotation

Page 246: Jpa Mit en

Hibernate-spezifische Annotationen

JPA mit Hibernate 245

A.13.5 Proxy

Signatur: @Proxy

Paket: org.hibernate.annotations

Ziel: Entity

Beschreibung: Legt die Lazy- und Proxy-Einstellungen für diese Klasse fest.

Parameter:

A.13.6 AccessType

Signatur: @AccessType

Paket: org.hibernate.annotations

Ziel: Klasse, Feld oder Methode

Beschreibung: Der AccessType eines Felds.

Parameter:

A.13.7 BatchSize

Signatur: @BatchSize

Paket: org.hibernate.annotations

Ziel: Klasse, Methode oder Feld

Beschreibung: Definiert die Batchsize der SQL-Anweisung.

Parameter:

Name Typ Beschreibung

lazy true/false Definiert die Strategie zum Laden der Klasse

proxy Klassenobjekt Die Klasse oder das Interface, das als Proxy-Objekt für diese Entität verwendet werden soll

Name Typ Beschreibung

value Zeichenkette Der Name des AccessType

Name Typ Beschreibung

size numerisch Die BatchSize

Page 247: Jpa Mit en

A – Referenz der Annotationen

246

A.13.8 Cache

Signatur: @Cache

Paket: org.hibernate.annotations

Ziel: Entity oder ein Feld mit einer Collection

Beschreibung: Legt die Caching-Strategie fest.

Parameter:

A.13.9 Cascade

Signatur: @Cascade

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Legt die Cascade-Strategie fest.

Parameter:

Name Typ Beschreibung

usage CacheConcurrencyStrategy Die zu verwendende Concurrency-Strategie. Mögliche Werte sind:� CacheConcurrencyStrategy.NONE � CacheConcurrencyStrategy.NONSTRICT_READ_WRITE � CacheConcurrencyStrategy.READ_ONLY� CacheConcurrencyStrategy.READ_WRITE� CacheConcurrencyStrategy.TRANSACTIONAL

include Zeichenkette Legt fest, welche Felder in den Second-Level-Cache aufgenommen werden sollen. Mögliche Werte sind:� all� non-lazy

region Zeichenkette Der Name der Region des Caches

Name Typ Beschreibung

value CascadeType[ ] Eine Liste von Cascade-Strategien. Mögliche Werte sind:� CascadeType.ALL� CascadeType.DELETE� CascadeType.DELETE_ORPHAN� CascadeType.EVICT� CascadeType.LOCK� CascadeType.MERGE� CascadeType.PERSIST� CascadeType.REFRESH� CascadeType.REMOVE� CascadeType.REPLICATE� CascadeType.SAVE_UPDATE

Page 248: Jpa Mit en

Hibernate-spezifische Annotationen

JPA mit Hibernate 247

A.13.10 Check

Signatur: @Check

Paket: org.hibernate.annotations

Ziel: Klasse, Feld oder Methode

Beschreibung: Definiert eine Constraints-Prüfung.

Parameter:

A.13.11 CollectionOfElements

Signatur: @CollectionOfElements

Paket: org.hibernate.annotations

Ziel: Klasse, Feld oder Methode

Beschreibung: Markiert eine Collection als Sammlung von Elementen bzw. als Samm-lung eingebetteter Objekte.

Parameter:

A.13.12 Columns

Signatur: @Columns

Paket: org.hibernate.annotations

Ziel: Klasse, Feld oder Methode

Beschreibung: Definiert eine Liste von @javax.persistence.Column Annotations.

Parameter:

Name Typ Beschreibung

constraints Zeichenkette SQL-Syntax zur Constraints-Prüfung

Name Typ Beschreibung

fetch FetchType Der Fetch Type des Felds. Folgende Werte sind erlaubt:� fetchType.lazy� fetchType.eager

targetElement Klassenobjekt Die Klasse der Elemente in dieser Sammlung

Name Typ Beschreibung

columns Column Eine Liste von @Column-Definitionen

Page 249: Jpa Mit en

A – Referenz der Annotationen

248

A.13.13 DiscriminatorFormula

Signatur: @DiscriminatorFormula

Paket: org.hibernate.annotations

Ziel: Entity

Beschreibung: Definiert eine Formel zur Unterscheidung der Entitäten.

Parameter:

A.13.14 Fetch

Signatur: @Fetch

Paket: org.hibernate.annotations

Ziel: Feld oder Methode

Beschreibung: Definiert eine Fetch-Strategie für die Assoziation.

Parameter:

A.13.15 Filter

Signatur: @Filter

Paket: org.hibernate.annotations

Ziel: Klasse, Feld oder Methode

Beschreibung: Fügt einen Filter zu einer Entität oder einer Assoziation hinzu.

Parameter:

Name Typ Beschreibung

value Zeichenkette Die Discriminator-Formel

Name Typ Beschreibung

value FetchMode Die Fetch-Strategie. Mögliche Werte sind:� FetchMode.JOIN� FetchMode.SELECT� FetchMode.SUBSELECT

Name Typ Beschreibung

name Zeichenkette Der Name des Filters, der verwendet werden soll

condition Zeichenkette Die Filterbedingung

Page 250: Jpa Mit en

Hibernate-spezifische Annotationen

JPA mit Hibernate 249

A.13.16 Filters

Signatur: @Filters

Paket: org.hibernate.annotations

Ziel: Klasse, Feld oder Methode

Beschreibung: Definiert eine Liste von @Filter-Definitionen, die zu einer Entität oder einer Assoziation hinzugefügt werden sollen.

Parameter:

A.13.17 FilterDef

Signatur: @FilterDef

Paket: org.hibernate.annotations

Ziel: Klasse oder Package

Beschreibung: Definiert einen Filter.

Parameter:

A.13.18 ParamDef

Signatur: @ParamDef

Paket: org.hibernate.annotations

Ziel: Innerhalb einer @FilterDef-Definition.

Beschreibung: Definiert einen Parameter für einen Filter.

Parameter:

Name Typ Beschreibung

value Filter[ ] Eine Liste von @Filter-Definitionen

Name Typ Beschreibung

name Zeichenkette Der Name des zu definierenden Filters

defaultCondition Zeichenkette Die Filterbedingung, die standardmäßig gilt

parameters ParamDef[ ] Eine Liste von @ParamDef-Annotationen

Name Typ Beschreibung

name Zeichenkette Der Name des Parameters

type Zeichenkette Der Typ des Parameters

Page 251: Jpa Mit en

A – Referenz der Annotationen

250

A.13.19 FilterDefs

Signatur: @FilterDefs

Paket: org.hibernate.annotations

Ziel: Klasse oder Package

Beschreibung: Definiert eine Liste von @FilterDef-Definitionen.

Parameter:

A.13.20 Formula

Signatur: @Formula

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Definiert eine Formel zum Anlegen einer Spalte. Diese Annotation ersetzt die @Column-Definition.

Parameter:

A.13.21 Generated

Signatur: @Generated

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Legt fest, dass das aktuelle Feld von der Datenbank generiert wird.

Parameter:

Name Typ Beschreibung

value FilterDef[ ] Eine Liste von @FilterDef-Definitionen

Name Typ Beschreibung

value Zeichenkette Eine SQL-Anweisung zur Definition der Spalte

Name Typ Beschreibung

value GenerationTime Legt fest, wann das Feld generiert wird. Mögliche Werte sind:� GenerationTime.ALWAYS� GenerationTime.INSERT� GenerationTime.NEVER

Page 252: Jpa Mit en

Hibernate-spezifische Annotationen

JPA mit Hibernate 251

A.13.22 GenericGenerator

Signatur: @GenericGenerator

Paket: org.hibernate.annotations

Ziel: Klasse, Methode, Feld oder Package

Beschreibung: Definiert einen Hibernate-Generator.

Parameter:

A.13.23 Parameter

Signatur: @Parameter

Paket: org.hibernate.annotations

Ziel: Innerhalb einer @GenericGenerator-Annotation

Beschreibung: Definiert einen Generator-Parameter.

Parameter:

A.13.24 IndexColumn

Signatur: @IndexColumn

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Definiert eine Indexspalte für eine Liste.

Parameter:

Name Typ Beschreibung

name Zeichenkette Der Name des Hibernate-Generators

strategy Zeichenkette Die zu verwendende Strategie

parameters Parameter[ ] Eine Liste von Generator-Parametern

Name Typ Beschreibung

name Zeichenkette Der Name des Parameters

value Zeichenkette Der Wert des Parameters

Name Typ Beschreibung

name Zeichenkette Der Name der Spalte

base numerisch Der Basiswert des Index

columnDefinition Zeichenkette Die Definition der Spalte

nullable true/false Legt fest, ob der Index null sein darf

Page 253: Jpa Mit en

A – Referenz der Annotationen

252

A.13.25 LazyCollection

Signatur: @LazyCollection

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Definiert den Lazy-Status einer Collection.

Parameter:

A.13.26 LazyToOne

Signatur: @LazyToOne

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Definiert den Lazy-Status einer „Zu-eins“-Beziehung.

Parameter:

A.13.27 MapKey

Signatur: @MapKey

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Definiert die Spalten für einen Key einer Map.

Parameter:

Name Typ Beschreibung

value LazyCollectionOption Optionen der Lazy-Strategie. Mögliche Werte sind:� LazyCollectionOption.EXTRA� LazyCollectionOption.FALSE� LazyCollectionOption.TRUE

Name Typ Beschreibung

value LazyToOneOption Mögliche Werte sind:� LazyToOneOption.FALSE� LazyToOneOption.NO_PROXY� LazyToOneOption.PROXY

Name Typ Beschreibung

columns Column[ ] Eine Liste von @javax.persistence.Column-Definitionen

Page 254: Jpa Mit en

Hibernate-spezifische Annotationen

JPA mit Hibernate 253

A.13.28 MapKeyManyToMany

Signatur: @MapKeyManyToMany

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Definiert die Spalten für einen Key einer Map.

Parameter:

A.13.29 NamedNativeQuery

Signatur: @NamedNativeQuery

Paket: org.hibernate.annotations

Ziel: Klasse oder Package

Beschreibung: Erweitert die @javax.persistence.NamedNativeQuery Annotation um Hibernate-spezifische Features.

Parameter:

Name Typ Beschreibung

joinColumns JoinColumn[] Eine Liste von @javax.persistence.JoinColumn-Definitionen

Name Typ Beschreibung

name Zeichenkette Der Name der Abfrage

query Zeichenkette Die Abfragedefinition

cacheable true/false Legt fest, ob der Query zwischengespeichert werden darf oder nicht

cacheMode CacheModeType Der Cache-Mode. Mögliche Werte sind:

� CacheModeType.GET� CacheModeType.IGNORE� CacheModeType.NORMAL

� CacheModeType.PUT� CacheModeType.REFRESH

cacheRegion Zeichenkette Der Name der Cache-Region

callable true/false

comment Zeichenkette Ein Kommentar, der zur SQL-Anweisung hinzugefügt wird

fetchSize numerisch Die Anzahl der Spalten, die der JDBC-Treiber laden soll

flushMode FlushModeType Mögliche Werte sind:

� FlushModeType.ALWAYS� FlushModeType.AUTO

� FlushModeType.COMMIT� FlushModeType.NEVER

readOnly true/false Legt fest, ob die Rückgabewerte der Abfrage nur gelesen werden dürfen

resultClass Klassenobjekt Die Klasse des Abfrageergebnisses

resultSetMapping Zeichenkette Der Name der Abbildung des Abfrageergebnisses auf die Klasse

timeout numerisch Der Timeout in Sekunden

Page 255: Jpa Mit en

A.13.30 NamedNativeQueries

Signatur: @NamedNativeQueries

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Definiert eine Liste von @NamedNativeQuery-Definitionen.

Parameter:

A.13.31 NamedQuery

Signatur: @NamedQuery

Paket: org.hibernate.annotations

Ziel: Klasse oder Package

Beschreibung: Erweitert die @javax.persistence.NamedQuery Annotation um Hiber-nate-spezifische Features.

Parameter:

Name Typ Beschreibung

value NamedNativeQuery[ ] Eine Liste von @NamedNativeQuery-Definitionen

Name Typ Beschreibung

name Zeichenkette Der Name der Abfrage

query Zeichenkette Die Abfragedefinition

cacheable true/false Legt fest, ob die Query zwischengespeichert werden darf oder nicht

cacheMode CacheModeType Der Cache-Mode. Mögliche Werte sind:� CacheModeType.GET� CacheModeType.IGNORE� CacheModeType.NORMAL� CacheModeType.PUT� CacheModeType.REFRESH

cacheRegion Zeichenkette Der Name der Cache-Region

comment Zeichenkette Ein Kommentar, der zur SQL-Anweisung hinzugefügt wird

fetchSize numerisch Die Anzahl der Spalten, die der JDBC-Treiber laden soll

flushMode FlushModeType Mögliche Werte sind:� FlushModeType.ALWAYS� FlushModeType.AUTO� FlushModeType.COMMIT� FlushModeType.NEVER

readOnly true/false Legt fest, ob die Rückgabewerte der Abfrage nur gelesen werden dürfen

timeout numerisch Der Timeout in Sekunden

Page 256: Jpa Mit en

Hibernate-spezifische Annotationen

JPA mit Hibernate 255

A.13.32 NamedQueries

Signatur: @NamedQueries

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Definiert eine Liste von @NamedQuery-Definitionen.

Parameter:

A.13.33 NotFound

Signatur: @NotFound

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Legt die Aktion fest, die ausgeführt wird, wenn ein Element einer Assozi-ation nicht gefunden wird.

Parameter:

A.13.34 OnDelete

Signatur: @OnDelete

Paket: org.hibernate.annotations

Ziel: Klasse, Methode oder Feld

Beschreibung: Definiert die Strategie beim Löschen eines Objekts aus einer Collection oder einem Array. Zusätzlich wird mit @OnDelete definiert, wie beim Löschen einer Joi-ned-Subclass verfahren werden soll.

Parameter:

Name Typ Beschreibung

value NamedQuery[ ] Eine Liste von @NamedQuery-Definitionen

Name Typ Beschreibung

action NotFoundAction Die Aktion. Mögliche Werte sind:� NotFoundAction.IGNORE� NotFoundAction.EXCEPTION

Name Typ Beschreibung

action OnDeleteAction Die Aktion. Mögliche Werte sind:� OnDeleteAction.CASCADE� OnDeleteAction.NO_ACTION

Page 257: Jpa Mit en

A – Referenz der Annotationen

256

A.13.35 OrderBy

Signatur: @OrderBy

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Definiert eine SQL-Anweisung zum Sortieren einer Collection.

Parameter:

A.13.36 Parent

Signatur: @Parent

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Definiert das Feld als Verweis zu seinem Besitzer.

Parameter: keine

A.13.37 Sort

Signatur: @Sort

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Sortiert eine Collection mittels des Java Collection API.

Parameter:

Name Typ Beschreibung

Clause Zeichenkette Die orderby-clause innerhalb der SQL-Anweisung

Name Typ Beschreibung

comparator Klassenobjekt Die Implementierung der java.util.Comparator-Schnittstelle

type SortType Der Sortierungstyp. Mögliche Werte sind:� SortType.COMPARATOR� SortType.NATURAL� SortType.UNSORTED

Page 258: Jpa Mit en

Hibernate-spezifische Annotationen

JPA mit Hibernate 257

A.13.38 Type

Signatur: @Type

Paket: org.hibernate.annotations

Ziel: Methode oder Feld

Beschreibung: Hibernate Erweiterung des Feldtyps.

Parameter:

A.13.39 TypeDef

Signatur: @TypeDef

Paket: org.hibernate.annotations

Ziel: Klasse oder Package

Beschreibung: Definiert einen Typ.

Parameter:

A.13.40 TypeDefs

Signatur: @TypeDefs

Paket: org.hibernate.annotations

Ziel: Klasse oder Package

Beschreibung: Definiert eine Menge von Typdefinitionen mit @TypeDef.

Parameter:

Name Typ Beschreibung

type Zeichenkette Der Name des Hibernate-Typs

parameter Parameter[ ] Eine Liste von @Parameter-Definitionen

Name Typ Beschreibung

name Zeichenkette Der Name des Typs

typeClass Klassenobjekt Die Klasse des Typs

parameter Parameter[ ] Eine Liste von @Parameter-Definitionen

Name Typ Beschreibung

value TypeDef[ ] Eine Liste von @TypeDef-Definitionen

Page 259: Jpa Mit en

A – Referenz der Annotationen

258

A.13.41 Where

Signatur: @Where

Paket: org.hibernate.annotations

Ziel: Klasse, Methode oder Feld

Beschreibung: Definiert eine Where-Clause, die innerhalb der SQL-Anweisung verwen-det werden soll.

Parameter:

Name Typ Beschreibung

clause Zeichenkette Die Where Clause

Page 260: Jpa Mit en

JPA mit Hibernate 259

B Literaturverzeichnis

� Robert Hien, Markus Kehle: „Hibernate und das Java Persistence API“, entwickler.press

� Bauer, Christian und King, Gavin: „Hibernate in Action”, Manning

� Böttcher, Ulrike und Frischalowski, Dirk: „Java 5 Programmierhandbuch“, Software & Support Verlag

� Enterprise-JavaBeans-Spezifikation, Version 3.0 – Java Persistence API: http://www.jcp.org/en/jsr/detail?id=220

� Spezifikation Java Persistence API 2.0: http://jcp.org/en/jsr/summary?id=317

� Hibernate Reference Documentation: http://www.hibernate.org

� Laddad, Ramnivas: „AspectJ in Action”, Manning

� Blog In Relation To… Hibernate Static Metamodel Generator Annotation Processor: http://relation.to/Bloggers/HibernateStaticMetamodelGeneratorAnnotationProcessor

Page 261: Jpa Mit en
Page 262: Jpa Mit en

JPA mit Hibernate 261

Stichwortverzeichnis

Symbole@Access 66@AccessType 245@AssociationOverride 226@AttributeOverride 77, 225@Basic 228@BatchSize 245@Cache 246@Cacheable 196@Cascade 246@Check 247@CollectionOfElements 99, 247@CollectionTable 99@Column 68, 77, 99, 225, 227, 247@ColumnResult 171, 216@DiscriminatorColumn 93, 240@DiscriminatorFormula 248@DiscriminatorValue 93, 241@ElementCollection 98, 102@Embeddable 76, 242@Embedded 76, 242@EmbeddedId 70, 222, 224@Entity 33, 55, 65, 209, 243@EntityListeners 122, 210@EntityResult 170, 216@Enumerated 98, 102, 106, 229@ExcludeDefaultListeners 210@ExcludeSuperclassListeners 210@Fetch 248@FieldResult 171, 216@Filter 248@FilterDef 249–250@Formula 250@Generated 250@GeneratedValue 55, 72–73, 222@GenericGenerator 72, 251@Id 33, 55, 70, 222@IdClass 70, 223@Index 244@IndexColumn 103, 251@Inheritance 93, 240@JoinColumn 85, 99, 226, 231, 234–237

@JoinTable 86, 99, 234@LazyCollection 252@LazyToOne 252@Lob 98, 102, 228@ManyToMany 87–88, 233@ManyToOne 88, 232@MapKey 100, 234–237, 252@MapKeyEnumerated 102@MapKeyManyToMany 253@MapKeyTemporal 102@MappedSuperclass 241@NamedNativeQuery 214, 253–254@NamedQuery 29, 153, 213, 254–255@NotFound 255@OnDelete 255@OneToMany 84, 88, 233@OneToOne 79, 81, 88, 232@OrderBy 104, 237–238, 256@OrderColumn 103, [email protected] [email protected] [email protected] [email protected] [email protected].

CollectionOfElements [email protected].

Entity 33, 68, [email protected] [email protected] [email protected].

GenericGenerator 33, [email protected].

OptimisticLock [email protected] [email protected] [email protected] [email protected] 69, [email protected].

DocumentId [email protected] [email protected].

Indexed 37

Page 263: Jpa Mit en

Stichwortverzeichnis

262

@ParamDef 249@Parameter 251@Parent 256@PersistenceContext 28, 62, 141, 217–218@PersistenceProperty 217@PersistenceUnit 142, 218@PersistentUnits 218@PostLoad 118, 212@PostPersist 118, 211@PostRemove 118, 212@PostUpdate 118, 212@PrePersist 118, 211@PreRemove 118, 211@PreUpdate 118, 212@PrimaryKeyJoinColumn 81, 220–221,

238–239@Proxy 245@QueryHint 213@Resource 142@SecondaryTable 67, 137, 220@SecondaryTables 67@SequenceGenerator 223@Sort 256@SQLResultSetMapping 30, 170–171, 215@StaticMetamodel 182@Table 55, 67, 219, 244@TableGenerator 224@Temporal 98, 102, 229@Transient 55, 67, 227@Type 257@TypeDef 257@UniqueConstraint 219, 224, 234@Version 133, 146, 229–230@Where 258

AAbfrageparameter 152ACID-Eigenschaften 127add-Methode 88Aggregat-Funktionen 166Aggregation 75Annotation 21Annotation-Parameter 21Antipattern 147Application-managed Entity Manager 28

Assoziation 78Assoziationstabelle 87

BBatch-Fetching 192benutzerdefinierter Mapping-Typ 203Beziehungen 18

CCaching-Strategien 197CascadeType 88close() 110Collection-Instanzvariablen 98commit 128ConnectionProvider 32Container-managed Entity Manager 28contains() 113CRUD 48, 53custom value types 201

DDatenbankidentität 70Datenbankintegrität 96Datenbanktransaktionen 127Dependency Injection 28Detached Entities 111DetachedCriteria 178Dialekt 46Dirty Read 128Discriminatorvalue 93

EEager Load 187EHCache 198EJB Query Language 29, 190EJB-3.0-Spezifikation 27Entities 27Entity Beans 27EntityManager 28, 32, 217EntityManagerFactory 29, 57, 217EntityTransaction 29equals 71Example 178extended persistence context 29

Page 264: Jpa Mit en

Stichwortverzeichnis

JPA mit Hibernate 263

FFetching-Strategien 187FetchType.EAGER 187FetchType.LAZY 187Filter 179find() 110, 112First Commit Wins 146flush() 112from 155

GGenerics 23Granularität 17Graphennavigation 19group by 167

Hhashcode 71Hibernate Annotations 33Hibernate Configuration 32Hibernate Core 33Hibernate EntityManager 35Hibernate Query Language 154Hibernate Validator 34hibernate.cfg.xml 45–46hibernate.properties 45HibernateUtil 48

IImpedance Mismatch 17Inner Join 190inverseJoinColumns 86isLoaded() 117Isolationsebene Serializable 129Isolationsebenen 128

JJava Persistence Query Language 29Java Transaction API 28JBoss Cache 198Join 163joinColumns 86JPQL 29JTA Transaktionen 129

KKardinalität 78Komponente 74Komposition 75Konfiguration 45

LLast Commit Wins 146Lazy Load 187Lazy Loading 115Left Join 192Left Outer Join 192LockMode 137Logging 43

MmappedBy 84Massen-Delete 169Massen-Update 169merge() 111, 113Meta-Annotations 22

NNeu in JPA 2.0

@Access 66@OrderColumn 103Basistypen in einer Map 102case Statement 162Collection-Parameter für in Operator 159Collections von Basistypen 98Criteria API 172, 180Datentypen für Primärschlüssel 70Datums- und Zeitliterale 157detach() 115Erweiterte Anwendung von

@JoinColumn 86Erweiterte Anwendung von

@JoinTable 86Erweiterung für @OrderBy 105Erweiterung im EntityManager 142Erweiterung von order by 163Erweiterungen des Query Interface 151isLoaded() 117Komponenten 76Locking 136orphanRemoval 85Parameter Interface 152Properties 57

Page 265: Jpa Mit en

Stichwortverzeichnis

264

Second Level Cache 196Type Funktion 168TypedQuery 150Überladen von find() 112Überladen von refresh() 111Validierung von Entities 122

Non-Repeatable Read 128

OObjektidentität 18Optimistisches Locking 132order by 162OSCache 198

PPerformance 187persist() 110, 112Persistence Provider 31, 39Persistent 110Persistenzkontext 28Pessimistisches Locking 133Phantom Read 128POJO 27, 47, 65polymorphe Abfragen 168Primärschlüssel 70Proxy 51

QQuery 29Query Cache 195

Rrefresh() 111remove() 110, 112Resource-Local Transaktionen 130Restrictions 173rollback 128

SSecond Level Cache Provider 198select 155, 165Session 32, 48SessionFactory 32, 48Subqueries 169Subselect-Fetching 194

TTransaction 32TransactionFactory 32transaction-scoped persistence context 29Transient 109

Uupdate 51UserCollectionType 97UserType 201

VValue-Typen 74, 97Verbindungstabelle 86–87Vererbung

JOINED 92SINGLE_TABLE 92TABLE_PER_CLASS 92

Vererbungsstrategie 92Volltextsuche 36

Wwhere 156

XXML-Mapping 39