Java Persistence API

© Arno Schmidhauser Letzte Revision: November 2006 Email: arno.schmidhauser@bfh.ch Webseite: http://www.sws.bfh.ch/db

Dieses Skript stützt sich wesentlich auf die Spezifikation JSR 220 Version 3.0 von Sun, Final Release, 2. Mai 2006 Inhalt I Wichtige Java Konstrukte II Übersicht Java Persistence API III Beziehungen zwischen Entities IV Spezielles V Objektverwaltung und Transaktionsmanagement VI JPA Query Language

JPA – Java Persistence API

Über diesen Kurs
• Inhalt – Anwendung des Persistence API von EJB 3.0 • Voraussetzungen – Java 1.4, JDBC, SQL • Nicht Ziele – Einbettung der Persistenztechnologie in Container/AppServer ( S. Fischli Business Tier)

2

Arno Schmidhauser

November 2006

Seite 2

JPA – Java Persistence API

I Wichtige Java Konstrukte

3

Java 5 hat einige wichtige Neuerungen. Drei davon, Enumerations, Annotations und Generics werden kurz vorgestellt. Diese Konstrukte sind für das JPA wichtig.

Arno Schmidhauser

November 2006

Seite 3

Exceptions und die noch zu besprechenen Annotations aufgeführt. In der Datenbank können wahlweise der Name des jeweiligen Elementes einer Enumeration (in obigen Beispielen "PERSIST". Enumerations sind in Java-API Dokumentation auf der gleichen Ebene wie Klassen.) • Eine Enumeration wird intern wie eine Klasse behandelt.WRITE usw. LockModeType. 1 usw. WRITE die Elemente der Aufzählungen. READ. zur Beschreibung verschiedenster Optionen. zum Beispiel für die Status-Beschreibung von Objekten. protected.) gespeichert werden.) oder die dahinterliegenden Ordnungszahlen (0. In diesen Beispielen sind CascadeType und LockModeType die Aufzählungen. Auch als Datentyp für persistente Daten kommen Enumerations in Frage. Im Rahmen der JPA Spez. CascadeType. und besitzt auch äusserlich die Eigenschaften einer Klasse: – Sie gehört zu einem Package – Sie kann public. private sein – sie kann Attribute und Methoden besitzen 4 Enumerations ersetzen die bisher verwendeten Konstanten in Form statischer Klassen. beispielsweise CascadeType. Interfaces.JPA – Java Persistence API Enumerations • Eine Aufzählung ist eine Klasse mit einer abschliessend definierten Menge von benannten Objekten.REMOVE. @Enumerated(STRING) meineEnumerationMember oder @Enumerated(ORDINAL) meineEnumerationMember Arno Schmidhauser November 2006 Seite 4 . • Wichtige Teile einer Enumeration sind: – Der Name (die Klasse) der Aufzählung als Ganzes – Die Namen der einzelnen Aufzählungselemente – Eventuell der zugrundeliegende Wert (int. "REMOVE" etc. PERSIST. LockModeType. werden Enumeration innerhalb von Annotations verwendet.READ.PERSIST. REMOVE.oder Interface-Variablen. Die nachfolgende Anotation beim jeweiligen Klassenmember einer Entitiy wird dazu verwendet. String usw.

out."). i < all. wenn sie denselben Namen haben. for( int i = 0. System.OPEN.valueOf( "OPEN" ). if ( s1 == s2 ) // liefert true Zeile 8: Die Methode valueOf() ermöglicht das dynamische Erzeugen eines EnumElements auf Grund eines gleichnamigen Strings. Die Definition ist in einem File Status.java abzulegen. 2.. Das folgend Stück Code liefert bei der Bedingung true als Ergebenis: Status s1 = Status. Zeile 3 und 4 erstellen je eine Variable (Objekt) dieser Enumeration. 10.toString() ). 3. 5. System.OPEN. Zeile 6 liefert die Zahl 0 als Ausgabe. i++ ) { System.CLOSED.println("Beide gleich. 7.. Arno Schmidhauser November 2006 Seite 5 . 8. Für das Element CLOSED wäre die Ausgabe die Zahl 1. 12.JPA – Java Persistence API Enumerations. Status s1 = Status. Beispiel 1 1.OPEN. Zeile 9: Die Methode values() liefert sämtliche Elemente der Enumeration. 11.ordinal() ). Zeile 5 liefert den String "OPEN" als Ausgabe.println( s1. 4.out.length.out. Zeile 7: Zwei Elemente sind gleich. Status s2 = Status. Status s2 = Status. public enum Status { OPEN. Status s3 = Status. } 5 Obiges Beispiel zeigt die Definition und den Gebrauch einer einfachen Enumeration: Zeile 1 definiert die Enumeration Status.out. CLOSED } .values(). if(s1 == s2) System.println( all[i].println( s1. 9. Status all[] = Status. 6.toString() ).

JPA – Java Persistence API Enumerations. wie bei jedem anderen Java-Objekt eine definierte Methode aufgerufen wird. protected String zusatzWert. return this.B. Status( String zusatzWert) { 5. public enum Status { 2. zusatzWert. Beispiel 2 Eigene Attribute und Methoden 1. } 6 Das Verhalten ist grundsätzlich gleich wie im vorhergehenden Beispiel. Ein Enumeration-Element kann im Code beispielsweise wie folgt deklariert werden: Status s = Status. 4. } 7. 6. OPEN( "offen" ).OPEN Zusätzlich kann von s die Methode getZusatzWert() aufgerufen werden.println( s2. 9. System. this. zusatzWert = zusatzWert. Der Konstruktor muss private oder unspezifiziert sein. } 10. Arno Schmidhauser November 2006 Seite 6 . public String getZusatzWert(){ 8. CLOSED( "geschlossen" ).out. 3. z.getZusatzWert() ).

• Annotations sind typsicher. sie werden vom Compiler geprüft. die nicht ausgeführt. Parametern. Klassen. aber zur Laufzeit abgefragt werden können. Membern. zu Annotationen und lokalen Variablen. Konstruktoren. • Annotations werden intern als Interfaces behandelt. • Annotations gehören zu Paketen.JPA – Java Persistence API Annotations • Annotations fügen dem Java-Code zusätzliche Informationen hinzu. 7 Arno Schmidhauser November 2006 Seite 7 .

Arno Schmidhauser November 2006 Seite 8 . Die Annotation ist einem File MyAnnotation. ist jedes Target erlaubt. 3. Zu 4: Wenn @Inherited angegeben ist. gilt die Annotation auch für abgeleitete Klassen. sind Annotations mit komplexer Struktur durchaus üblich (z. 8. Beispiel für Definition 1.B. soll die Annotation in Dokumentation-Tools (javadoc) dokumentiert werden. String[] wann().java abzulegen und zu kompilieren. @Target(ElementType. worauf die Annotation angewendet werden kann. sind nicht erlaubt. ansonsten tritt ein Compilerfehler auf.RUNTIME) @Inherited @Documented @interface MyAnnotation { String was() default "Testen". Exceptions und Enumerations.OPEN. Im Rahmen der JPA Spez. Status status() default Status. oder auch in der laufenden Applikation(RUNTIME). In diesem Fall auf eine Klasse oder ein Interface (TYPE). @Retention. 2. weil es Annotationen für Annotationen sind.lang. zusätzlich im kompilierten ByteCode(CLASS). 9. ob die Annotation nur im Source-Code (SOURCE). 4. andere Annotationen. ausser die Klasse Class. Enumerations. Zu 7-9: Als Felder der Annotation können Strings. @Inherited und @Documented heissen MetaAnnotationen. Zu 3: Die Retention (übersetzt etwa "Vorbehalt" oder "Halterung") bestimmt. import java. 10. 5. Zu 6: Die eigentliche Annotation. Default ist CLASS. Zu 2: Das Target bestimmt. Andere Klassen. 6. Ist kein Target angegeben.JPA – Java Persistence API Annotation. @JoinTable).*. Die Annotationen @Target. } 8 In diesem Beispiel wird eine Annotation definiert. primitive Typen. Interfaces. Zu 5: Wenn @Documented angegeben ist.TYPE) @Retention(RetentionPolicy. 7. Felder ohne Default müssen beim Gebrauch der Annotation angegeben werden. oder Arrays von all diesen angegeben werden.annotation. wie Klassen. Im JPA haben alle Annotations die Retention RUNTIME. Eine Klasse kann auch ohne Weiteres mehrere vorangestellte Annotationen haben.

dass die Angaben bereits zur Kompilationszeit typengeprüft werden können. insbesondere über das Mapping von Klassen-Feldern auf Datenbank-Felder. 3. dass unter Umständen sehr viel Meta-Information im Source Code steht. Arno Schmidhauser November 2006 Seite 9 . 2. respektive muss neu aufgebaut werden.. } 9 Mit diesem Stück Code ist der Klasse MyAnnotatedClass eine Annotation des Typs MyAnnotation zugewiesen. @MyAnnotation( was="Pruefen". Der Nachteil liegt darin.JPA – Java Persistence API Annotation. Beispiel für Anwendung 1. Damit kann der Source Code sehr unübersichtlich werden.. Die gesamte Persistenz-Information im JPA wird via Annotations definiert. 4. wann={ "Anfang". "Ende" } ) public class MyAnnotatedClass { // . 5. Der Vorteil liegt darin.

out. wann=[Anfang.annotationType() ein Objekt der Klasse Class abholen.MyAnnotation(status=OPEN. System. was=Pruefen.toString() ). 8. und dieses dann mittels des Reflection API weiter analysieren. Man kann daher mit der Methode Annotation. Class c = Class. } 9. Ende]) Die toString()-Methode ist für die Analyse von Annotationen meist zu ungenau.println( a. throws Exception 4. { 5. public class AnnotationsAbfragen { 2.getAnnotations()) { 7. } 10.JPA – Java Persistence API Annotations abfragen 1. public static void main( String args[] ) 3. for (Annotation a : c.forName("MyAnnotatedClass"). 6. Arno Schmidhauser November 2006 Seite 10 . } 10 Mit der toString()-Methode erhält man eine textuelle Darstellung zurück: @pmq.

JPA – Java Persistence API Generics • Bis Java 1. – schwach dokumentiert. 11 Arno Schmidhauser November 2006 Seite 11 . list.4: Object als generische Klasse.get(0) • Vorteil: übersichtlich und einfach. – Häufige Cast-Operation erforderlich. Beispiel: List list = new LinkedList(). String s = (String) list. list. • Nachteile: – nicht typsicher. Klasse der Collection unklar.add( new Integer( 7 ) ). eine Collection kann fälschlicherweise Objekte verschiedener Klassen enthalten.add( new String( "foo" ) ).

JPA – Java Persistence API

Generics
• Ab Java 5: Generische Klassen mit Angabe des konkreten Typs zum Zeitpunkt der Verwendung. Beispiel: List<String> list = new LinkedList<String>(); list.add( new String( "foo" ) ); list.add( new Integer( 7 ) ); // Compiler Fehler String s = list.get(0) // kein Cast nötig • Im Rahmen der Java Standard Edition treten Generics vorallem im Package java.util auf, z.B. List<T>, Set<T>, Map<K,V>, Comparator<T>, Iterator<T> usw.

12

Die alte Form kann nach wie vor verwendet werden. Der Compiler erzeugt jedoch eine Warnung. Die Angabe List<T> heisst generische Klasse Die Angabe T heisst Typvariable Die Angage String heisst Typargument Die Angabe List<String> heisst generischer Typ

Arno Schmidhauser

November 2006

Seite 12

JPA – Java Persistence API

Eigene Generics, Definition
public class Node<T> { protected T content; protected Node<T> parent; public Node( T content ) { this.content = content; } public T getContent() { return( this.content ); } public Node<T> getParent() { return parent; } public void setParent( Node<T> parent ) { this.parent = parent; }
13

Sehr oft hat eine generische Klasse nur einen Typparameter. Es ist aber durchaus möglich, mit mehreren zu arbeiten. Beispielsweise: public class Edge<K, T> { protected Node<T> left, right; protected K weight; public Edge( Node<T> left, Node<T> right, K weight ) { this.left = left; this.right = right; this.weight = weight; } } Weil K ein beliebiges Typargument aufnehmen kann, z.B. Double oder Integer, aber auch String, können keine spezifischen Methoden, wie etwa das Zusammenzählen von Kantengewichten implementiert werden. Dieses Problem lösen die Generics mit Wildcards (siehe weiter hinten). Generische Klassen können abgeleitet werden wie andere Klassen. Diese Situation ist beispielsweise bei Collections anzutreffen: TreeSet ist von AbstractSet, dieses von AbstractCollection abgeleitet. public class SpecialNode<T> extends Node<T> { public SpecialNode( T content ) { super( content ); } Für generische Interfaces gilt dasselbe wie für generische Klassen.

Arno Schmidhauser

November 2006

Seite 13

JPA – Java Persistence API

Generics, Variablen
// korrekt Node<Integer> ni = new Node<Integer>( 7 ); Node<Double> nd = new Node<Double>( 3.14 ); Node<String> ns = new Node<String>( "Heinz" ); System.out.println( ni.toString() ); // nicht korrekt, Kompilationsfehler: Node<Number> n = new Node<Integer>( 1 ); // geht auch nicht, Kompilationsfehler: Node<Number> n; n = (Node<Number>) new Node<Integer>( 1 );

14

Ein Ziel der Generics ist die Möglichkeit einer strengen Typenprüfung zur Kompilationszeit. Werden generische Klassen, die mit unterschiedlichen Typparametern instanziert sind, als inkompatibel angesehen, ist diese strenge Typprüfung gewährleistet, und es können Laufzeitfehler, wie man sie von der Vererbung her kennt, vermieden werden. Typroblem bei der normalen Vererbung: Number ni = new Integer( 1 ); Number nd = (Double) ni; // Kompilation ok, aber dynamischer Fehler!

Dieses Typproblem kann bei Generics nicht auftreten: Node<Number> nti = new Node<Integer>( 1 ); // Kompilations-Fehler! Node<Number> ntd = (Node<Double>) nti; // Kompilations-Fehler!

Arno Schmidhauser

November 2006

Seite 14

15 Der erste Fall betrifft die Kompatibilität der generischen Typen selbst. Hier will man natürlich nicht vom bisherigen Verhalten von Java abweichen. list1. Erlaubt: List<Person> list1 = new LinkedList<Person>(). Nicht erlaubt: List<Person> list1 = new LinkedList<Manager>().add( new Manager() ).add( new Person() ). Ein Objekt der untypisierten Klasse Person kann jederzeit durch ein Objekt der abgeleiteten und untypisierten Klasse Manager vertreten werden. Hier gilt das im vorhergehenden Beispiel gesagte.JPA – Java Persistence API Generics und Vererbung • Generics führen keine neue Art der Verbung ein! 1. Die Verwandtschaft von Typargumenten begründet keine Verwandtschaft der generischen Typen als Ganzes. 2. Damit wird eine hohe Typsicherheit zum Kompilationszeitpunkt erreicht. list1. Der zweite Fall betrifft die Kompatibilität in einer normalen Vererbungshierarchie. Arno Schmidhauser November 2006 Seite 15 .

} 16 Im Fall 1 kann die Variable x nun einen beliebigen generischen Typ aufnehmen. dass das Typargument sicher alle Methoden der Klassen Number kennt.JPA – Java Persistence API Generics. wenn er eine Zahl enthält. // content ist mindestens Number } } Die Modifikation von Objekten über eine Wildcard-Variable oder einen Typebound ist nicht erlaubt. dass ein Node-Typ nur Sinn macht.doubleValue(). Die Variable x kann gemäss Fall 1 und Fall 2 ein Objekt der Klasse NodeNumber entgegennehmen. Im Fall 2 kann man die möglichen Zuweisungen an die Variable x weiter einschränken. Beispielsweise kann man sagen. Wildcard. dass als Typargument eine Zahl angegeben wird. list. public double getDouble() { return aNumber. Einschränkung des Typparameters einer generischen Klasse: public class NumberNode< T extends Number > { . Node<?> x = new Node<Integer>( 7 ).setContent( 5 ). Wildcards 1. In diesem Fall kann man in der Klassendefinition bereits darauf abstellen. Wildcardvariable zur Aufnahme irgendeines Typs einer generischen Klasse: Node<?> x = new Node<Integer>().. // Kompilations-Fehler Arno Schmidhauser November 2006 Seite 16 . 2. public class NumberNode<T extends Number> { protected T aNumber.add( 123 ). x = new Node<Integer>(). weil x keine Kenntnis über den erlaubten Parameteryp von setContent() hat. anders. Die Situation ist gegenüber der Deklaration Objekt o = new Node<Integer>(). // Kompilations-Fehler // ebenso zum Beispiel für Listen List<?> list = new LinkedList<Integer>. eingeschränkt auf Zahlen: Node<? extends Number> x. indem x nur noch Nodes mit Zahlentypen entgegennehmen kann. // Fehler 3. Der folgende Code führt zu einem Kompilationsfehler. indem x nur Node-Typen aufnehmen kann. // ok x = new Node<String>(). Im Fall 3 wird der Typparameter einer generischen Klasse eingeschränkt (sogenannter Typebound). x.. und man kann diese auch im Code verwenden. In diesem Beispiel stellt die Methode getDouble() darauf ab.

public class MySorter { public static void main( String args[] ) { List<Double> myList = new Vector<Double>(). myList. Collections.out.14 ) ). Der Comparator c muss Objekte vom Typ T vergleichen können. womit die Deklaration Comparator<? super T> nun verständlich ist. Beispiel: import java.util.toString() ).JPA – Java Persistence API Frage: Wie ist folgende Deklaration aus der Java Standard Edition zu lesen? Collections.compareTo( new BigDecimal(n2. Comparator<? super T> c ) Antwort: Die Operation sort() kann eine Liste vom Typ T sortieren.sort( List<T> list. K muss also gleich T oder eine Oberklasse von T sein. Damit dies möglich ist.sort( myList. Number ist ein Obertyp von Double.*. müssen die Parameter der Methode compare( K obj1. kann ohne weiteres der NumberComparator auf Double angewendet werden. Arno Schmidhauser November 2006 Seite 17 . Double n2 ) { return Double.compare( n1.41 ) ). n2 ).toString())) ). new DoubleComparator() ). Number n2 ) { return ( (new BigDecimal(n1. könnte man einen allgemeineren Comparator für Number erstellen. } } Statt einen Comparator für Double-Werte.println( d.toString())). K obj2 ) den Typ T oder eine Unterklasse davon haben.add( new Double( 3. } } } class DoubleComparator implements Comparator<Double> { public int compare ( Double n1. } } Da ein Double ein Spezialfall einer Number ist.add( new Double( 1. Dieser Comparator kann auch sehr grosse Zahlen miteinander vergleichen: class NumberComparator implements Comparator<Number> { public int compare ( Number n1. myList. for ( Double d : myList ) { System.

Würden zur Laufzeit den Klassenvariablen verschiedene Typen von Objekten zugewiesen. was grundsätzlich über Reflection möglich ist. Weil sich alle Typen einer generischen Klasse eine einzige Implementation dieser Klasse teilen. wie der Compiler für die Übersetzung anderer Java-Klassen benötigt. Aus Sicht der JVM ist die Typinformation für das Laufzeitsystem entfernt. Um den Typ einer Collection festzustellen. Primitive Typen können zur Laufzeit nicht als Objekte vom Typ Object behandelt werden. } Keine Abfrage der Typinformation zur Laufzeit mit Reflection. Das Reflection API kann das Typargument nicht eruieren. entstünden völlig unvorhersgesehene Resultate. sind die statischen Attribute nur einmal vorhanden und tragen zur Laufzeit den Typ Object. Keine Arrays von Typvariablen erlaubt. die zur Kompilationszeit vorgenommen werden. Arrays von Typvariablen sind nicht möglich. und damit die Speicherallokation nicht definiert ist. • Nach der Kompilation wird die Typinformation mehrheitlich gelöscht im Byte-Code: // Vor Kompilation class Node<Number> { private T content. Jedoch kann der Typ von Objekten an sich ermittelt werden. Primitive Typen (int. Statische Attribute können keinen generischen Typ haben. byte. müsste man den Typ der Objekte darin ermitteln.JPA – Java Persistence API Generics. char) können nicht als Typargumente verwendet werden. weil der Typ zu Laufzeit nicht mehr bekannt ist. } // Nach Kompilation class Node { private Object content. Eigenschaften • Generics sind Typkontrollen. Integer und String. Primitive Typen sind daher nicht erlaubt. double. 18 Nach der Kompilation wird nur noch soviel Typinformation über die Klasse und ihre Methoden in den Bytecode eingebaut. Arno Schmidhauser November 2006 Seite 18 . zum Beispiel Double. Statische Attribute können daher keinen Typ haben. respektiv dynamische ClassCastExceptions beim Lesen und Schreiben dieser Objekte. was aber mit der Art der Realisierung von Generics in Java notwendig wäre.

JPA – Java Persistence API II Übersicht Java Persistence API 19 Referenz: JSR 220. Arno Schmidhauser November 2006 Seite 19 . EJB 3.0 Java Persistance API. Final Release 2. Mai 2006 Kurzform in diesem Skript: JPA oder JAPI Spez.

x Technologien. Outer Join Fetching • SQL-Generierung zur Laufzeit 20 Mit der Lösung des JPA in EJB 3.0 wurden wesentliche Verbesserungen ( ↑ ) erreicht werden gegenüber EJB 2.JPA – Java Persistence API Moderne Persistenz API's ↑ Arbeiten mit gewöhnlichen Java-Klassen für Daten (POJO's) ↑ Objekte können transient oder persistent sein ↑ Innerhalb und ausserhalb von Appservern verwendbar ↑ Vererbung. Arno Schmidhauser November 2006 Seite 20 . Das JPA ist ein Mix der Erfahrungen aus dem EJB 2. den Source Code 100% von Mapping und Verhaltensangaben frei zu halten.x Ansatz und dem JDO Konzept. Aggregation. JDO ist ein sehr ambitiöser Ansatz. mit dem Anspruch. Komposition abbildbar ↑ Transitive Persistenz oder Persistence by Reachabiltiy • Lazy Fetching • Automatic Dirty Checking • Datenbank-Roundtrips minimieren.

& Annotations EJB 3. dass für jede Zugriffsoperation (Zuweisung eines Wertes. Dieser Classloader modifiziert den Code so. z. . dass das Objekt modifiziert wurde (Dirty Checking) und letztlich muss aufgezeichnet werden. Increment/Decrement. Als Realisierungstechniken kommen typischerweise Source Code Modifikationen beim Deployment oder Byte Code Enhancement zur Kompilations. Set und List werden durch eigene Hibernate-Typen ersetzt. indem jede Datenklasse über einen eigenen Classloader geladen wird. Dann muss aufgezeichnet werden. Dereferenzierung. Mit Ihnen wird das Mapping und das Verhalten der persistenten Klassen gesteuert. Wichtige Datentypen. SQL 2003 oder Dialekte Auf der logischen Ebene sind Java-Annotations das Kernstück des JPA.Driver SQL RDB 21 Java 1. JDBC 3.B. Dieses Verhalten ist gut im Debugger ersichtlich! Arno Schmidhauser November 2006 Seite 21 . Hibernate verwendet Byte Code Enhancement zur Laufzeit.JPA – Java Persistence API Technologie Stack Session Beans / Java Applikation Persistence API Persistence API Implemetation JDBC API JDBC . ob ein Objekt zum CommitZeitpunkt transient oder persistent ist. Im letzteren Fall muss sein Zustand in die Datenbank zurückgeschrieben werden.. Die Anforderungen des Dirty Checking.oder Laufzeit in Frage. dass das Objekt sich überhaupt im Speicher befindet (Lazy Fetch.0 Spezifikation Hibernate & CGLIB. Array-Index-Auflösung) zusätzlich Verwaltungs-Code eingeschoben wird.0 Herstellerspez. Lazy Fetching und der transitiven Persistenz erfordern viel Aufwand in der JAPI Implementation: Wenn immer ein Programm eine Modifikation an Datenobjekten vornimmt. Eager Fetch).. muss vorerst sichergestellt werden.

spi. im Container-Umfeld mit @PersistenceContext EntityManager em. Ein Programm beginnt daher seine Persistenz-Aufgaben mit: EntityManagerFactor emf = Persistence. Im Container-Umfeld übernimmt dieser die Aufgabe der Klasse Persistence.persistence. Mit dem Transformer wird den geladenen Klassen zusätzliche Funktionalität für das Management der Persistenz hinzugefügt. den Transformer auf.spi definiert drei Interfaces für das Management der persistenten Klassen. Sie erzeugt eine EntityManagerFactory und in diesem Zug interne Instanzen von spi.persistence ist für die Entwicklerseite. EntityManager em = emf.persistence javax.PersistenceUnit und spi.persistence. Die Klasse Persistence ist quasi die Bootstrap-Klasse (im Container-Umfeld übernimmt dieser die Funktion von Persistence).createEntityManagerFactory( persistenceUnitName ).ClassTransformer. Arno Schmidhauser November 2006 Seite 22 .persistence. die mit Entities arbeiten. Dieser ruft für die Entity-Klassen und alle Klassen. bevor diese Klassen in der JVM aktiv werden.spi nichts zu tun.JPA – Java Persistence API JPA • Packages javax. das Pakage javax. Ausserdem wird ein neuer ClassLoader in der JVM installiert.spi • Classes Persistence • Interfaces EntityManagerFactory EntityManager EntityTransaction Query • Runtime Exceptions ( ~8 ) RollbackExceptione ption • Annotations ( ~64 ) Entity Id OneToOne OneToMany • Enumerations ( ~10 ) InheritanceType CascadeType FetchType 22 Das Package javax.PersistenceProvider.createEntityManager() resp. Der Entwickler hat mit dem Package javax.

indem das Byte-Code Enhancement beispielsweise abgeleitete Klassen erzeugt. kein Interface und keine Enumeration sein und keine final-Methoden enthalten. 2. 5.Methoden auf Felder zugegriffen wird. Zugriff von Clients auf Felder nur über get/set. Klasse darf nicht final. 3. und in diesen Methoden das Persistenz-Management stattfinden kann. Arno Schmidhauser November 2006 Seite 23 . Anforderungen: 1. No-Arg Konstruktur muss vorhanden sein.oder Business-Methoden erlaubt. 4. 23 Die Anforderungen 1 bis 4 sind technisch bedingt. Die Anforderung 3 garantiert. Jede Entity muss einen Primärschlüssel (@Id) haben.JPA – Java Persistence API Entities • • • • • Kennzeichnung mit Annotation @Entity Klasse kann Basisklasse oder abgeleitet sein Klasse kann abstrakt oder konkret sein. Felder müssen private oder protected sein. mit denen eine Applikation dann effektiv arbeitet. Sie erlauben vernünftige Realisierungen der Persistenz. dass nur über die –abgeleiteten. Serialisierbarkeit ist bezüglich Persistenz nicht erforderlich.

. sind möglich. protected String receiver. public Message() {} // get. ob das Mapping in ein Datenbankattribut als DATE. @Temporal für die Java Typen Date und Calendar.. Mit folgenden Annotations kann man andere Mappings vornehmen: @Entity @Table( name="MyMessageTable" ) public class Message { // . } Zahlreiche weitere Mappingangabe. Mit @Temporal wird angegeben.und Schema-Name bei der Tabelle. Für gewisse Datentypen kann man Präzisierungen anbringen. protected int priority. Beispiel @Entity public class Message { @Id protected long id.1 JPA Spez. @Column( name="senderName" ) protected String sender. Datenbank.. Arno Schmidhauser November 2006 Seite 24 . @Temporal( TemporalType. protected String sender.TIMESTAMP ) protected Date date. Siehe Kapitel 9.. TIME oder TIMESTAMP verstanden werden soll. z. z. // .B. set and business methods } 24 Sehr pragmatisch gelöst ist das Mapping der Felder auf Datenbanktabellen: Ohne zusätzliche Angaben werden der Klassenname als Tabellenname und die Feldnamen als Spaltennamen verwendet.B.JPA – Java Persistence API Entity.

sind ebenfalls persistent.. Während einige vordefinierte Typen wie Date. findet bei eigenen serialisierbaren Klassen einfach nur eine Ablage des serialisierten Byte-Stromes in der Datenbank statt (varbinary Datentyp o. also z. Calendar. Solche Objekte können nicht in einer Abfragebedingung von Queries auftreten. String – Alle Wrapperklassen und serialisierbaren Typen (z. List<>. • Nicht erlaubt: – Alle Arten von Arrays ausser die obgenannten – Collections von etwas anderem als Entities. Byte[].ä. Hibernate wesentlich weiter geht als die JPA Spez.B. BigDecimal usw. Für technische Anwendungen könnte das Verbot von Array-Typen lästig sein. Transiente Felder können mit Java transient oder mit der @Transient Annotation markiert werden. dass z. insbesondere auch Arrays von primitiven Typen. Zur Problematik der Elementreihenfolge in Listen siehe Kapitel IV. Calendar) – byte[].B. in die entsprechenden SQL-Typen gemapped werden. sei es von Java vordefinierte oder eigene Klassen.B. Character[] – Enumerations – Beliebige weitere Entity-Klassen – Collections von Entities. Set<> oder Map<> deklariert sind.JPA – Java Persistence API persistente Datentypen • erlaubt: – Alle primitiven Typen. Arno Schmidhauser November 2006 Seite 25 . Wrapperklassen und andere serialiserbare Typen. Felder von serialisierbaren Klassen. Integer. Date. char[]. BigDecimal. 25 Der Umfang an persistenten Typen ist für die meisten Anwendungen ausreichend. indem Arrays und Collections für sämtliche Arten von Objekten erlaubt sind. welche als Collection<>. Dazu ist allerdings zu sagen.). Integer.

z. columnDefinition="VARCHAR(255) NOT NULL" ) protected String sender. @Column( name="sender".B. 26 JDBC 3..0 Mapping Arno Schmidhauser November 2006 Seite 26 ...0..JPA – Java Persistence API Java-Type / SQL-Type Mapping • Implizit durch JDBC 3. . . definiert. • Produktspezifisch durch Framework (Hibernate) oder im JDBC-Driver für die jeweilige Datenbank. Data Type Conversion Table. • Explizit durch die @Column Annotation.

JPA – Java Persistence API Access-Typ • Es gibt zwei Zugriffspunkte für das Persistenz-Framework auf die Daten einer Klasse: das eigentliche Datenfeld. Arno Schmidhauser November 2006 Seite 27 . Bei Field-based Access können get-Methoden oder anders benannte Methoden für den Zugriff auf private Felder verwendet werden.} } 27 Welche Zugriffsart vorliegt. } • Property-based Access @Entity public class Message { protected long id. ist durch die Position der Mapping-Annotations gegeben.} public void setId( long id ) {..und get-Methoden zwingend paarweise vorkommen. Bei Property-based Access wird in der Datenbank der von der get()-Methode zurückgelieferte Wert abgelegt. so enthält die Datenbanktabelle den Personennamen in Grosschrift. @Id public long getId() {. oder die zugehörigen set()/get() Methoden... beim Erzeugen des Objektes in der Applikation wird die set()-Methode für das Setzen des Zustandes verwendet. Wäre also beispielsweise ein Personename in einem Java-Objekt in Kleinschreibung abgelegt. die getMethode liefert jedoch immer eine Grosschreibung zurück. Bei Property-basedAccess müssen alle set. • Field-based Access @Entity public class Message { @Id protected long id.. Alle Mapping Annotations müssen entweder bei Feldern stehen ( Field-based Access) oder bei der get() Methode für eine Eigenschaft.

JPA – Java Persistence API

Entity Identity - Primärschlüssel
• Jede Entity-Klasse muss einen mit @Id bezeichneten Primärschlüssel besitzen. • Primärschlüssel können in Zusammenarbeit mit der Datenbank generiert werden. Beispiel: @Entity public class Message { @Id @GeneratedValue(strategy=IDENTITY ) public long id; • Stragegien sind Identity, Table, Sequence und Auto

28

Identity und Sequence: Das Datenbankssystem erzeugt eine fortlaufende Nummer. Sequence lehnt sich an die Oracle-Technologie an. Auto: vom Framework gewählte Strategie. Meist gleichbedeutend mit Identity. Table: Erzeugung via Tabelle in Datenbank. Diese Tabelle wird durch den Entwickler, resp. DBA, erstellt und initialisiert. Beispiel: public class Message { @TableGenerator( name="MyIdGenerator", table="KeyStore", pkColumnName="keyName", valueColumnName="keyValue", pkColumnValue="MessageId", initialValue=1000, allocationSize=1) @Id @GeneratedValue( strategy=GenerationType.TABLE, generator="MyIdGenerator") protected long id; // ... } create table KeyStore ( keyName nvarchar( 32 ) not null unique, keyValue bigint not null default 1 ); insert into KeyStore ( keyName, keyValue ) values ( 'MessageId', 1000 );

Arno Schmidhauser

November 2006

Seite 28

JPA – Java Persistence API

Zusammengesetzte Primärschlüssel
• Es wird eine spezielle ID-Klasse benötigt. Beispiel: public class DocumentId implements Serializable { protected String name; protected BigDecimal version; // verlangte Methoden … @IdClass( pmq.DocumentId.class ) // Variante 1 @Entity public class Document { @Id protected String name; @Id protected BigDecimal version; @Entity public class Document { // Variante 2 @EmbeddedId public DocumentId docId;

29

Die ID-Klasse ist notwendig, damit ein einheitlicher Gebrauch aller Arten von Primärschlüsseln möglich wird, z.B. beim Aufruf der find() oder getReferenceMethode(), um bestimmte Objekte in der Datenbank zu finden. Genau genommen wird ja auch bei einfachen Primärschlüsseln mit einer ID-Klasse gearbeitet, allerdings von einem vordefinierten Java-Typ. An die ID-Klasse werden folgende Anforderungen gestellt: 1. No-arg Konstruktor 2. Serializable 3. Der Access-Typ (field oder property) muss dem Access-Typ in der verwendeten Entity-Klasse entsprechen. 4. Fields oder properties müssen public oder protected sein. 5. Die hashCode() und equals()-Methode müssen implementiert werden. 6. Name und Typ der einzelnen Felder/Properties müssen in der ID-Klasse und in der verwendeten Entity-Klasse übereinstimmen, wenn nicht @EmbeddedId verwendet wird. Bedingung 5 kann wie folgt realisiert werden: public int hashCode() } public boolean equals( Object o ) DocumentId oid = (DocumentId) o; return ( this.name.equals( oid.name ) && this.version.equals( oid.version ) ); } { {

return (name + version.toString()).hashCode();

Arno Schmidhauser

November 2006

Seite 29

JPA – Java Persistence API

Eine Applikation darf nicht direkt die Felder eines ID-Objektes ändern (DocumentIDObjektes), sondern nur die entsprechenden Felder in der Entity-Klasse (Document), wenn mit @IdClass gearbeitet wird. wenn mit @EmbeddedID gearbeitet wird, kann ein Primärschlüsselobjekt zum Beispiel im Konstruktor der Datenklasse wie folgt erzeugt werden: public Document( String name, BigDecimal version, StringBuffer content ) { this.docId = new DocumentId( name, version ); // ... } Die zugehörige Datentabelle sieht in beiden Fällen(@IdClass oder @EmbeddedId) wie folgt aus: create table "Document" name version -- ... primary key ( name, version ) ); (

nvarchar(64) not null, numeric(4,2) not null default '1.0',

Arno Schmidhauser

November 2006

Seite 30

sqlserver.password" value="hibuser"/> <property name="hibernate.HibernatePersistence</provider> <class>pmq. können in den Properties die üblichen JDBC-Verbindungsparameter angegeben werden.connection.connection.xml abgelegt.hibernate.Message</class> <non-jta-data-source>jdbc/MyPMQDB</non-jta-data-source> <jta-data-source>jdbc/MyPMQDB</jta-data-source> <properties> <property name="hibernate.SQLServerDriver"/> <property name="hibernate.username" value="hibuser"/> <property name="hibernate. 31 Der Name ist eine Identifikation.ejb. Je nachdem ob die Datenquelle JTA-fähig ist (verteiltes Transaktionsmanagement) verwendet man den Tag <jta-data-source> oder <non-jata-data-source>.jdbc.databaseName=hib_db"/> <property name="dialect" value="org.PMQ</class> <class>pmq. Beispiel: <persistence> <persistence-unit name="PMQ" transaction-type="RESOURCE_LOCAL"> <provider>org.connection. Der Transaktionstyp (transaction-type) wird als JTA oder RESOURCE_LOCAL angegeben. Falls nicht mit JNDI gearbeitet wird. um in der Applikation dafür eine EntityManagerFactory erstellen zu können. Die Datenquelle kann als JNDI-Name angegeben werden im Rahmen einer J2EE-Umgebung.JPA – Java Persistence API Persistence Unit • Eine Persistence Unit ist eine logische Einheit von Entities.microsoft.hibernate.connection.dialect. eine JTA Persistence Unit muss u. Der PersistenceProvider ist die implementationsspezifische "Urklasse" für das Persistenzmanagement in dieser Persistence Unit.SQLServerDialect"/> </properties> </persistence-unit> </persistence> Arno Schmidhauser November 2006 Seite 31 . Sie wird beschrieben durch: – – – – – – – Einen Namen Die zu dieser Unit gehörenden Entity-Klassen Angaben zum Persistence Provider Angabe zum Transaktionstyp Angaben zur Datenquelle Weitere Properties Namen von XML OR-Mapping Files • Technisch wird die Beschreibung einer Persistence Unit in der Datei META-INF/persistence. verteilte Transaktionen über eine XA-Schnittstelle erlauben. Sie liefert die EntityManagerFactory zurück.driver_class" value="com.A.url" value="jdbc:sqlserver://localhost. Die Entity-Klassen werden als Information für den Class Transformer und die Generierung der SQL-Befehle angegeben.

// ..getTransaction().EXTENDED) public class MessageProducer { // . EntityManagerFactory emf = Persistence.JPA – Java Persistence API Persistence Context • Der Persistence Context definiert das physische Umfeld von Entities zur Laufzeit: – Die Menge aller Managed Entities in der Applikation – Der Entity Manager für diese Entities – Die laufende Transaktion – Der Contexttyp @PersistenceContext(type=PersistenceContextType. 32 Arno Schmidhauser November 2006 Seite 32 . Transaction tx = em.createEntityManager(). EntityManager em = emf....createEntityManagerFactory("PMQ").

Arno Schmidhauser November 2006 Seite 33 . wird aufbewahrt.3.B.). Probleme mit generierten Primärschlüsseln verursachen. Das kann z. weil der lokale Zustand beibehalten. remove() usw. – Propagation von Efffekten und Änderungen in die DB aber nur. Dasselbe gilt sinngemäss für Versionennummern bei optimistischem Concurrency Control. Ein Rollback führt in beiden Contexttypen dazu.JPA – Java Persistence API Persistence Context. wenn nachträglich begin()/commit() ausgeführt wird. 33 Bei Hibernate ist für Java SE Applikationen immer der Transaktionstyp EXTENDED gesetzt. Contexttyp • Contexttyp TRANSACTION – Lesender und schreibender Zugriff nur innerhalb der Transaktion. Dies gilt auch für den Contexttyp Extended. • Contexttyp EXTENDED – Alle Objekte sind lesend und schreibend zugreifbar. – Effekt von persist(). – Wiedereinkopplung in eine Transaktion mit merge(). 3. – Modifikationen finden lokal statt. weil eine bereits vergebene ID seitens Datenbank nochmals vergeben wird. Nach einem Rollback sollte nicht mehr mit den bestehenden Enitities gearbeitet werden (Kap. dass bereits geladene Entities in den Zustand detached versetzt werden.2 JPA Spez. – Gelesene Objekte sind nach der Transaktion im Zustand detached. der Zustand in der DB jedoch zurückgesetzt wird. Datenbankzugriffe arbeiten gemäss Spezifikation im Isolationsgrad read committed.

tx.... 34 Arno Schmidhauser November 2006 Seite 34 . // .commit(). tx. EntityTransaction tx = em. )..persist( m ).createEntityManagerFactory("PMQ")..JPA – Java Persistence API Erzeugen persistenter Objekte // .. Message m = new Message( .begin(). EntityManager em = emf. em.getTransaction(). EntityManagerFactory emf = Persistence.createEntityManager().

) pmq..remove( pmq ). em. PMQ pmq = new PMQ( "Dringendes" ).persist( pmq ). ob die persist() Methode vor oder nach der append()-Methode aufgerufen wird! 35 Arno Schmidhauser November 2006 Seite 35 .. em. m wird ebenfalls aus der db gelöscht • Es spielt keine Rolle... Message m = new Message( .JPA – Java Persistence API Kaskadierte Persistenz (1) • Kaskadierte Persistenz heisst: Alle von einem persistenten Objekt aus erreichbaren Objekte sind ebenfalls persistent.append( m ). m wird automatisch persistent // .

JPA – Java Persistence API Kaskadierte Persistenz (2) • In JPA muss die Kaskadierung deklariert werden.PERSIST. nicht etwa auf das Ein.. public class PMQ { @OneToMany( cascade={CascadeType.REFRESH resp.. mit CascadeType.MERGE.oder Austragen in der Message-Liste mit gewöhnlichen Java-Methoden! 36 Die Kaskadierung kann auch auf die Methoden EntityManager. } • Achtung: Die Kaskadierung bezieht sich nur auf die persist() und die remove()-Methode.REMOVE } ) List<Message> messages. Arno Schmidhauser November 2006 Seite 36 . // . • Die Kaskadierung kann für das Erstellen und das Löschen der Persistenz separat eingestellt werden.refresh() und EntityManager.merge() bezogen werden. CascadeType. CascadeType.

Long.begin(). das heisst eine Liste von Arrays zurückgeliefert wird.createQuery( "select m from Message m where m..getReference(Message.. Die Typsicherheit würde sich durch Verwendung von Typparametern nicht erhöhen. wenn das Objekt nicht existiert (spätestens beim ersten eigentlichen Zugriff auf das Objekt).iterator().. Die Methode find() liefert null. Message m = em. Dies deshalb. Das Resultat wird als Liste zurückgegeben. List list = qry. Im Weiteren ergäben sich Schwierigkeiten. Die resultierende Liste hat keinen Typparameter... tx.getResultList(). wenn das Objekt nicht existiert.. Iterator it = list.id = 1 ). // oder . Long. // oder . Arno Schmidhauser November 2006 Seite 37 .find( Message. Ein Array von Typvariablen ist in Java nicht erlaubt.parseLong(1) ). Die Methode find() lädt auf jeden Fall die unmittelbaren Felder des gesuchten Objektes..commit().. // .. 37 Es sind zwei Varianten für das Auffinden von Objekten vorgestellt: Suchen via Primärschlüssel mit find() oder getReference() Es muss die Klasse und der Primärschlüsselwert im gleichen Datentyp wie in der Entity definiert übergeben werden. weil der Resultattyp vom Query abhängt und dieses in jedem Fall dynamisch ausgewertet wird.class. die Methode getReference() erzeugt eine Exception.. Suchen mit einer Abfrage Objekte werden via eine Abfrage gesucht. tx. Query qry = em.parseLong(1)).class. // . Message m = em.JPA – Java Persistence API Aufsuchen persistenter Objekte // . wenn die select-Klausel mehrere Objekte enthält.

2. Datenbanktabellen erstellen.JPA – Java Persistence API Aufgaben • Übung Persistent Message Queue PMQ. Testen. ev. Arno Schmidhauser November 2006 Seite 38 . Erstellen einer Grundausstattung. Zusatzprogramm zum erstellen einer leeren PMQ (PMQInit.java).. 5. persistence. Producer (MessageProducer. 3.java) für Messages programmieren. Hibernate-Umfeld und Datenbank aufsetzen.* «Entity» Message sender receiver priority date id 38 Aufgaben 1.xml zusammenstellen. Entity-Klassen PMQ und Message definieren. 6.java) und Consumer (MessageConsumer. MessageProducer main() «verwendet» MessageConsumer main() «verwendet» «Entity» PMQ id qname append() get() getAll() remove() isEmpty() size() messages 0. 4.

JPA – Java Persistence API III Beziehungen zwischen Entities 39 Arno Schmidhauser November 2006 Seite 39 .

Wird beispielsweise bei einer bidirektionalen Beziehung eine Message in einer Message Queue eingetragen. Many to Many Vererbung Aggregation. muss das Programm selber in der Message die Referenz auf die MessageQueue nachführen. Sie müssen jedoch deklariert werden. respektive dem Entwickler.JPA – Java Persistence API Beziehungen • Beziehungen zwischen Entities sind prinzipiell gegeben durch entsprechende Referenzen oder Collection-Member in den Entity-Klassen. bidirektional One to One. Das Persistenz-Framework tut das nicht. und sehr oft sind Details zum O/R-Mapping und zum Verhalten notwendig. • Folgende Beziehungs-Charakteristiken spielen eine Rolle: – – – – Unidirektional. Die Deklaration der Beziehungen ist eine Mapping-Hilfe für das Framework und bringt keine zusätzliche Programmautomation mit sich! Arno Schmidhauser November 2006 Seite 40 . Many to One. One to Many. Komposition • Der korrekte Unterhalt der Beziehungen ist Sache des Programmes! 40 Der Unterhalt der Beziehungen obliegt dem Programm.

not null Arno Schmidhauser November 2006 Seite 41 . // . bigint nvarchar(64) bigint not null identity primary key. dass der Fremdschlüssel nicht null sein darf.. 41 Die Beziehung geht rein von der linken Seite aus.. Die Owner-Klasse beinhaltet keine Angaben zu dieser Beziehung. create table "PMQ" ( id qname owner_id ). Es ist an sich nicht spezifiziert. dass immer ein Owner vorhanden sein muss. not null unique. Sie wurde hier nur aus Übersichtgründen weggelassen. Die Angabe bezieht sich also auf das Zielobjekt. In der Tabelle sollte sich dies darin wiederspiegeln. ob ein Owner mehrere PMQ's oder nur eine PMQ haben kann. Unidirektional «Entity» PMQ id 1. Die obige Tabelle darf durchaus eine foreign key Klausel enthalten. Mit @OneToOne( optional=false) wird präzisiert.1 owner id «Entity» Owner @Entity public class PMQ { @OneToOne( optional=false ) @JoinColumn( name="owner_id" ) protected Owner owner. Die Abbildung wird mit einem Fremdschlüssel in der PMQ-Tabelle realisert.JPA – Java Persistence API One To One..

Der Besitzer ist in diesem Beispiel die PMQ-Klasse. Damit können nicht zwei PMQ-Einträge in der Datenbank mit demselben owner_id existieren.1 1.. Die Angabe mappedBy="owner" bezieht sich auf das Feld owner in der Klasse PMQ. welche nicht die mappedBy-Angabe enthält. // . //. und damit ist indirekt gesagt. Mit optional=true kann das Owner-Objekt bestehen bleiben. Arno Schmidhauser November 2006 Seite 42 . indem ein uniqueConstraint auf den owner_id Fremdschlüssel gesetzt wird. und die Owner-Tabelle muss nicht angepasst werden. @Entity public class Owner { @OneToOne( mappedBy="owner". Bei bidirektionalen Beziehungen spricht man oft vom Besitzer. Der Besitzer ist die Klasse.. Bidirektional «Entity» PMQ id 0. dass der Fremdschlüssel sich auf der PMQ-Tabelle befindet.. wenn die PMQ gelöscht wird.. Auch die Datenbanktabelle für PMQ bleibt sich gleich.JPA – Java Persistence API One To One.1 owner id «Entity» Owner @Entity public class PMQ { @OneToOne( optional=false ) @JoinColumn( name="owner_id" ) protected Owner owner. optional=true ) protected PMQ pmq. Programmatisch wird nicht kontrolliert.. 42 Die Klasse PMQ bleibt gleich wie in der unidirektionalen Variante.. ob eventuell dasselbe Owner-Objekt für zwei PMQ-Objekte verwendet wird. Dies ist nur zu kontrollieren.

. aus einer Kombination von Tabellenname links + id-Feld links. -. ). nicht mit einer direkten Primärschlüssel-/ Fremdschlüssel-beziehung.. inverseJoinColumns={ @JoinColumn(name="messages_id") } ) protected List<Message> messages = new Vector<Message>(). Obige Angaben in der @JoinTable-Annotation entsprechen dem Default-Verhalten: Der Name der Assoziationstabelle setzt sich aus den Namen der beiden EntityKlassen zusammen. 43 Eine unidirektionale OneToMany-Beziehung wird mit einer Assoziationstabelle abgebildet. sondern vielleicht noch zu anderen Klassen gehören. Die Attributnamen der Assoziations-Tabelle ergeben sich wie im folgenden Beispiel für die Tabellendefinition gezeigt.* messages id «Entity» Message @Entity public class PMQ { @OneToMany @JoinTable( name="PMQ_Message". bigint bigint not null. braucht es nicht für jede Zugehörigkeit in der Message-Tabelle ein Fremdschlüssel-Attribut... not null unique bigint not null primary key. von denen dann viele nicht belegt sind. create table "PMQ" ( id -. // . create table "Message" ( id ). joinColumns={ @JoinColumn( name="PMQ_id" ) }... Wenn die Zugehörigkeit in eine (oder mehrere) Assoziationstabelle ausgelagert ist. Beziehungsname links + id-Feld rechts. Arno Schmidhauser November 2006 Seite 43 . Unidirektional «Entity» PMQ id 0. Begründung an diesem Beispiel: Eine Message kann nicht nur zu einer PMQ..JPA – Java Persistence API One To Many... create table "PMQ_Message" ( PMQ_id messages_id ). bigint not null identity primary key.

//... müssen programmatisch und allenfalls über Triggers in der Datenbank realisiert werden (check-Constraints sind nicht ausreichend... Andere Multiplizitäten. Die @JoinColumnAnnotation deklariert den Namen des Fremdschlüssels.1 Beziehung: Es wird entweder @ManyToOne( optional=false ) oder @ManyToOne( optional=true ) angegeben. bigint bigint not null identity primary key.n. Zu beachten ist auch der Unterschied zwischen einer ManyToOne 1. In der Message-Klasse ist die Rückbeziehung durch die @ManyToOne-Annotation deklariert.. Achtung: Mit nur dem Java-Code wäre nicht zweifelsfrei festgehalten.. Diese Angabe wird durch mappedBy="pmq" in der OneToMany-Annotation vorgenommen. )..n.JPA – Java Persistence API One To Many. Auf der Many-Seite gibt es also nur die Multiplizität 0. Ausserdem ist die Tabellendefinition für den Fremdschlüssel mit null respektive not null zu deklarieren. 44 Die bidirektionale Beziehung wird als enger wie die unidirektionale angesehen und deshalb per Default als direkte Primärschlüssel-/Fremdschlüssel Verknüpfung realisiert. @Entity public class Message { @ManyToOne( optional=true ) @JoinColumn( name="PMQ_id" ) protected PMQ pmq.. Bidirektional «Entity» PMQ id messages 0. Für die @OneToMany-Annotation gibt es keine optional=false respektive optional=true Angabe....pmq könnte ja auf eine andere PMQ zeigen als diejenige von der es referenziert wird. dass es sich um eine echt bidirektionale Beziehung handelt.. welches Feld (nicht Tabellenattribut!) in der Message-Klasse für die Beziehung verantwortlich ist. da der delete-Befehl keine Prüfung von check-Constraints auslöst). Hingegen wird in der Klasse PMQ angegeben. null -.1 oder 0. Die Annotation @JoinTable entfällt..1 0.* «Entity» Message id pmq @Entity public class PMQ { @OneToMany( mappedBy = "pmq" ) protected List<Message> messages = new Vector<Message>(). //. insbesondere 1. create table "Message" ( id PMQ_id -. Das Feld Message. und eine andere Bedeutung haben.foreign key to PMQ table Arno Schmidhauser November 2006 Seite 44 .

JPA – Java Persistence API Many To One...* «Entity» Address id owner @Entity public class Address { @ManyToOne( optional=false ) @JoinColumn( name="Address_id".1 0. Arno Schmidhauser November 2006 Seite 45 . Unidirektional «Entity» Owner id 1. referencedColumnName = "id" ) protected Owner owner.. Das obige Beispiel gibt diese Default-Benennung wieder. // . Der Default-Name für das Fremdschlüssel-Attribut ist der Name der Ursprungsklasse + "_" + Name des Primärschlüsselfeldes in der Zielklasse.. 45 Die unidirektionale ManyToOne-Beziehung dürfte eher selten sein.

..JPA – Java Persistence API Many To Many.. ausser dass der zweite Fremdschlüssel nicht unique ist: create table "PMQ_Message" ( PMQ_id messages_id ). Für die @ManyToMany-Annotation gibt es keine optional=false respektive optional=true Angabe. not null unique Arno Schmidhauser November 2006 Seite 46 . Andere Multiplizitäten. Die Message-Klasse enthält damit das Feld pmqs gar nicht. // . joinColumns={@JoinColumn(name="pmqs_id" ) }. Abbildung mit Assoziationstabelle.* 0. not null 46 Die unidirektionale ManyToMany-Beziehung entspricht aus Sicht Java Code der unidirektionalen OneToMany-Beziehung.. die Message kennt diese jedoch nicht.. Auf obiges Beispiel bezogen kann eine Message also zu mehreren Queues gehören.n. Es existiert also nur die Multiplizität 0. Bidirektional «Entity» PMQ «Entity» Message @Entity id id messages pmqs public class PMQ { 0. da der delete-Befehl keine Prüfung von check-Constraints auslöst). bigint bigint not null. Seitens Datenbank ist die Situation ebenfalls identisch zur OneToMany Beziehung. Nur die PMQ's kennen ihre Messages.. // .. @Entity public class Message { @ManyToMany( mappedBy = "messages" ) protected List<PMQ> pmqs = new Vector<PMQ>(). bigint bigint not null.n.* @ManyToMany @JoinTable( name="PMQ_Message". inverseJoinColumns={@JoinColumn(name="messages_id") } ) protected List<Message> messages = new Vector<Message>().. create table "PMQ_Message" ( pmqs_id messages_id ). müssen programmatisch und allenfalls über Triggers in der Datenbank realisiert werden (check-Constraints sind nicht ausreichend. insbesondere 1.

* «Entity» Message @OneToMany( cascade={ CascadeType. Arno Schmidhauser November 2006 Seite 47 . CascadeType. «Entity» PMQ id messages id 0..REMOVE}) protected List<Message> messages.1 Beziehung von rechts nach links zu Inkonsistenzen auf der Datenbank kommen..1 Beziehung von rechts nach links.JPA – Java Persistence API Komposition / Aggregation «Entity» PMQ id messages id 0. 47 Die Komposition ist in der Regel ergänzt sein um eine 1.* «Entity» Message @OneToMany( cascade={CascadeType. weil innerhalb des Programmes die beteiligten Objekte noch korrekt verknüpft sind. nicht die Message gelöscht wird. Achtung: Ohne Angabe der kaskadierten Löschung kann es auch mit einer deklarierten 1.remove( pmq ) würde nämlich klaglos ablaufen. im obigen Fall zum Beispiel: @ManyToOne( optional=false ) protected PMQ pmq.PERSIST} ) protected List<Message> messages..PERSIST. im obigen Fall zum Beispiel: @ManyToOne( optional=true ) protected PMQ pmq. Der Befehl em. in der Datenbank jedoch nur die PMQ.. Datenbankseitig könnte man dies durch einen not null constraint auf dem Fremdschlüssel in der Message-Tabelle verhindern. Die Aggregation kann ergänzt sein um eine 0.1 Beziehung von rechts nach links..

JPA – Java Persistence API Vererbung. • Klassen können abstrakt oder konkret sein. • Es gibt vier Mappingstrategien auf die Datenbank: – – – – Eine einzige Tabelle für die gesamte Verbungshierarchie Eine Tabelle für jede konkrete Klasse Eine Tabelle für jede Klasse Mapped Superclass 48 Zu den Strategien Die Strategie wird in der Basisklasse angegeben mit: @Entitiy @Inheritance(strategy=SINGLE_TABLE) @Entitiy @Inheritance(strategy=TABLE_PER_CLASS) @Entitiy @Inheritance(strategy=JOINED) @MappedSuperclass Arno Schmidhauser November 2006 Seite 48 . Grundsätzliches • Vererbungshierarchien können problemlos verwendet und abgebildet werden. • Alle Klassen in der Vererbungshierachie müssen den Primärschlüssel der Basisklasse verwenden (erben).

Anwendung: Hauptinformation in der Basisklasse. Es muss dann in jeder abgeleiteten Tabellen nach einem entsprechenden Datensatz gesucht werden. Flexible Datenstruktur für unterschiedlichste Bedürfnisse und Abfrage-Arten erforderlich. Die Attribute einer abstrakten Basisklasse werden in jeder Tabelle der abgeleiteteten Klasse erstellt. dass ein Objekt in mehreren Klassen vorkommt! Beispielsweise kann in einem CRM-System eine Person gleichzeitig Kunde und Lieferant sein. Mehrstufige Vererbungshierarchie. Die Felder werden in den Tabellen der abgeleiteten Entity-Klasse abgelegt. zu welcher Klasse ein Datensatz in der Basistabelle gehört.JPA – Java Persistence API SINGLE_TABLE Strategie Die Attribute der Basisklasse gelten für jeden Datensatz. Es ist ein Typ-Attribut erforderlich. dass für jeden Datensatz angibt. Attributzuordnung zu Klasse nicht ersichtlich. Fehlverhalten möglich. Vorteile: Jede Tabelle entspricht genau einer Klasse. Nachteile: Viele Attribute ohne Belegung mit Werten. indem Attributen Werte zugeordnet werden. Meistens werden nur konkrete und einzelne. Anwendung: Basisklasse nur schwach belegt und abstrakt. Abfragen über alle Objektarten sind einfach zu handhaben. wenn der Datensatz ein Objekt dieser Klasse repräsentiert. welche nicht transient deklariert sind. Änderungen an der Basisklasse erfordern Änderungen in allen Tabellen der abgeleiteten Klassen. TABLE_PER_CLASS Strategie Es wird nur pro konkrete Klasse eine Tabelle erstellt. Die abgeleiteten EntityKlassen übernehmen die Felder von der Basisklasse. Für die Basisklasse selbst wird keine Tabelle erstellt. Mapped Superclass Strategie Die Basisklasse ist selbst keine Entity. Vorteile: Konzeptionelles Modell im Tabellenmodell noch ersichtlich. Nachteil: Abfragen über mehrere Klassen hinweg erfordern union-Abfragen (kann teilweise Probleme mit SQL geben). Mit dieser Variante ist es datenbankseitig möglich. wie in den abgeleiteten Klassen vorhanden. Es sind keine nicht-belegten Attribute vorhanden. sowohl in der Basisklasse. Der Primärschlüssel der Basistabelle wird in allen abgeleiteten Klassen ebenfalls als Primärschlüssel verwendet und gleichzeitig als Fremdschlüssel auf die Basistabelle. oder insgesamt wenig Attribute zu implementieren. Nachteile: Gewisse Arten von Abfragen sind umständlich. wenn die Basisklasse nicht als eigene Tabelle und nicht als Entity in Erscheinung treten soll (Praktische Beispiele?) @MappedSuperclass public class MeineBasisklasse{} @Entity public class MeineAbgeleiteteKlasse extends MeineBasisklasse{} Arno Schmidhauser November 2006 Seite 49 . Anwendung: Viele Attribute. Vorteile: Kompakt. Kann angewendet werden. abgeleitete Klassen nicht zu umfangreich. stellt aber Attribute zur Verfügung für die abgeleiteten Klassen. Punktuelle Änderungen am Konzept haben nur punktuelle Änderungen in den Tabellen zur Folge. Attribute von abgeleiteten Klassen sind nur belegt. beispielsweise wenn man wissen will. abgeleitete Klassen in einer Applikation benötigt. zu welcher Klasse das repräsentierte Objekt gehört. welche als Entities deklariert sind. Häufige Änderung des Datenmodells wahrscheinlich. JOINED Strategie Jede Tabelle enthält nur die Attribute ihrer Klasse. die gar keine haben dürften → Integritätsbedingungen erforderlich.

Beispiel «Entity» Message id «Entity» SimpleMessage content «Entity» ImageMessage image contentType «Entity» XMLMessage xmlContent 50 Arno Schmidhauser November 2006 Seite 50 .JPA – Java Persistence API Vererbung.

dürfen null-Werte enthalten. welche nicht zur Basisklasse gehören. ist der Klassenname der Default.. @Entity @DiscriminatorValue("SM") public class SimpleMessage extends Message {} @Entity @DiscriminatorValue("IM") public class ImageMessage extends Message {} … 51 Falls kein Discriminatorattribut angegeben wird. not null. SINGLE_TABLE-Mapping @Entity @Inheritance(strategy=InheritanceType.JPA – Java Persistence API Vererbung. null.SINGLE_TABLE) @DiscriminatorColumn( name="messageType" ) public abstract class Message { @Id @GeneratedValue protected long id. create table "Message" ( id messageType sender receiver priority date content image contentType description xmlContent ). Alle Tabellenattribute. ist "DTYPE" der Default. not null. null. // . not null. bigint varchar( 64 ) nvarchar( 64 ) nvarchar( 64 ) int datetime nvarchar( max ) varbinary( max ) nvarchar( 64 ) nvarchar( 255 ) xml not null default primary key. not null. not null. null. Falls kein Discriminatorwert angegeben wird. null.. null Arno Schmidhauser November 2006 Seite 51 .

contentType nvarchar( 64 ) not null. xmlContent xml not null foreign key ( id ) references Message( id ) ). create table "XMLMessage" ( id bigint not null primary key.. null. image varbinary( max ) not null. null.JOINED) public abstract class Message { @Id @GeneratedValue protected long id.JPA – Java Persistence API Vererbung. @Entity public class SimpleMessage extends Message {} @Entity public class ImageMessage extends Message {} … 52 create table "Message" ( id bigint sender nvarchar( 64 ) receiver nvarchar( 64 ) priority int date datetime ). JOINED-Mapping @Entity @Inheritance(strategy=InheritanceType. create table "ImageMessage" ( id bigint not null primary key. foreign key ( id ) references Message( id ) ). not not not not not null identity primary key. create table "SimpleMessage" ( id bigint not null primary key. content nvarchar( max ) not null. Das TABLE_PER_CLASS Mapping funktioniert sinngemäss. foreign key ( id ) references Message( id ) ).. null. // . Arno Schmidhauser November 2006 Seite 52 . null.

unidirektional. Komposition ManyToMany. Aggregation Vererbung 53 Arno Schmidhauser November 2006 Seite 53 . Aggregation OneToMany. bidirektional.JPA – Java Persistence API Aufgaben • Implementieren Sie die verschiedenen Beziehungstypen am PMQ-Beispiel: – – – – OneToOne. unidirektional.

JPA – Java Persistence API IV Spezielles 54 Arno Schmidhauser November 2006 Seite 54 .

• Die Abbildung von Listen in Tabellen ist im JPA nicht vollständig transparent. • Datenbanktabellen haben keine Ordnung. sondern der Assoziationstabelle abgelegt ist. Damit kann die Positionierung auch im Fall einer ManyToMany-Beziehung abgebildet werden. das vom Framework gepflegt wird. sondern direkt mit Fremdschlüssel/Primärschlüssel erstellt wird. Hibernate Lösung @OneToMany @IndexColumn(name="position" ) List<Message> messages.JPA – Java Persistence API Listen • Listen haben eine implizite Ordnung. @OrderBy erfordert ausserdem ein geeignetes Attribut für die Sortierung (das vom Programm unterhalten wird).annotations. Arno Schmidhauser November 2006 Seite 55 . sondern muss durch Annotations beschrieben werden. JPA Lösung @OneToMany @OrderBy( "date ASC" ) List<Message> messages. Die Sortierung wird nicht automatisch aufrecht erhalten zur Laufzeit. wo eine Message in verschiedenen PMQ's verschiedene Positionen haben kann. Wird kein @OrderBy angegeben.hibernate. Mit IndexColumn kann ein spezielles. generisches Attribut definiert werden. Dies hat den Vorteil. dass die Positionierung nicht in der Entity-Tabelle (Message). findet die Sortierung zum Ladezeitpunkt nach dem Primärschlüssel statt. wenn das Mapping nicht mit einer Assoziationstabelle. Bei Hibernate ist @OrderBy nur erlaubt.IndexColumn. Es wird daher das Arbeiten mit der Hibernate-Annotation @IndexColumn empfohlen. 55 Für die @IndexColumn-Annotation ist noch die Hibernate-spezifische Annotation zu importieren: import org.

Wird beispielsweise keine @IndexColumn definiert in Hibernate. wird beim Anfügen eines neuen Elementes auch nur der neue Datensatz in der Tabelle eingefügt. Das Einfügen eines Elementes zwischendurch hat jedoch einen Update auf allen Einträgen der Assoziationstabelle zur Folge: Für jede Position wird der Fremdschlüssel des Elementes an dieser Position neu gesetzt. not null. position ). not null. unique ( messages_id ). werden alle Beziehungsdatensätze beim Zurückschreiben in die Datenbank gelöscht und neu eingefügt. foreign key ( PMQ_id ) references PMQ( id ) on delete restrict foreign key ( messages_id ) references Message( id ) on delete restrict ) Arno Schmidhauser November 2006 Seite 56 . Hibernate generiert für eine OneToMany Beziehung folgende Beziehungstabelle für die Liste: create table PMQ_Message ( PMQ_id messages_id position bigint bigint bigint not null. Ist die @IndexColumn definiert. primary key ( PMQ_id.JPA – Java Persistence API Die Abbildung von Listen tendiert zu Performance-Problemen.

joinColumns = { @JoinColumn( name="pmqs_id" ) }.annotations. // … Beispiel für das Arbeiten mit Arrays in Hibernate: import org.JPA – Java Persistence API Neben Listen werden auch Maps unterstützt.hibernate. bigint datetime smallint not null. not null. // … @Entity public class PMQ { @JoinTable( name="PMQ_Message". Message> messages.IndexColumn. Beispiel: import org. // … create table "EventDate" ( id eventDate position ).oder Fremdschlüssel.IndexColumn. Der Nutzen von Maps für Entities ist allerdings fraglich. // … @OneToMany @JoinTable( name="EventDate". joinColumns = @JoinColumn(name="id".hibernate. Maps ersetzen lediglich das Suchen von Objekten via Primär. referencedColumnName="id") ) @Column( name="eventDate" ) @IndexColumn( name="position" ) protected Calendar eventDates[] = new Calendar[10].annotations. not null Arno Schmidhauser November 2006 Seite 57 . inverseJoinColumns = { @JoinColumn( name="messages_id" ) } ) @MapKey( name="position" ) protected Map<Long.

ORDINAL) protected MessageStatus status..STRING) 58 Default für die Datenbankablage ist ORDINAL. wenn bei bestehenden Daten der Ablagetyp geändert wird Tabellenwerte gemäss aktueller Einstellung der @Enumerated-Annotation anpassen. varchar ist sowohl für den Stringwert wie für die Zahl geeignet. Arno Schmidhauser November 2006 Seite 58 . Anmerkung: Hibernate hat Schwierigkeiten mit der Datenbank. oder // . @Enumerated(EnumType.JPA – Java Persistence API Persistente Enumerations • Enumerations können persistent sein. In der Datenbank wird entweder der Ordinalwert (Position) oder der Stringwert (Name der Konstante) abgelegt. @Entity public class MutableMessage extends SimpleMessage { @Enumerated(EnumType. Der Datentyp in der Datenbank kann varchar oder int sein..

// . welche Serializable und nicht als Entities gekennzeichnet sind. Embedded Classes sind eine echte Komposition im UML-Sinn. Solche Felder sind jedoch nicht abfragbar mit JPA Query Language. Die Felder der eingebetten Klassen kommen explizit in den SQL-Tabellen vor... } 59 Klassen. Die Einbettung von Collections ist nicht unterstützt. create table "PMQ" ( id -.. varchar( 32 ) not null bigint not null identity primary key.. Embedded Klassen müssen als solche markiert sein. können in andere Klassen eingebettet sein: Ihre Felder werden in der Datenbank in derselben Tabelle abgelegt wie das Mutterobjekt. //.JPA – Java Persistence API Embedded Classes • Komposition: Mutterobjekt mit eingebetteten Objekten.. @Entity public class PMQ { @Id @GeneratedValue private long id. Die eingebetteten Objekte haben keine Identität und sind nicht shareable. respektive deren Objekte. } @Embeddable public class Owner { protected String name.. Die Einbettung hat (im Gegensatz zu JDO) nichts mit Serialisierung zu tun. • Eingebettete Objekte haben keine eigene Identität. Leider nicht für OneToMany Beziehungen. werden automatisch in die Datenbank serialisiert (Binärer Datentyp in der Datenbank). • Mutterobjekt und eingebettete sind in derselben Tabelle abgelegt.. @Embedded protected Owner owner. Selbstdefinierte Klassen. name ). Arno Schmidhauser November 2006 Seite 59 .

pkJoinColumns = { @PrimaryKeyJoinColumn(name="idRef". 60 Sollen mehrere Sekundärtabellen verwendet werden. @SecondaryTable(.. ..). name="receiver" ) protected String receiver. • Eine Entity kann beliebig viele Sekundärtabellen haben: @Entity @Table( name = "Message" ) @SecondaryTable( name = "MessageExt". ).)... } ) verwendet werden.. Ohne Vererbung klappten die Tests jedoch einwandfrei.. create table "MessageExt" idRef bigint -.. create table "Message" id ). name="sender" ) protected String sender.... Für jedes Feld in der Klasse muss die Herkunft spezifiziert werden. Arno Schmidhauser November 2006 Seite 60 . //... ( not null identity primary key... bigint -..JPA – Java Persistence API Sekundär-Tabellen für Entities • Gelegentlich wird eine Entity in der Datenbank in mehrere Tabelle hineinmodelliert. beispielsweise: public class Message { @Column( table="Message". muss die Annotation @SecondaryTables( {@SecondaryTable(. @Column( table="MessageExt". referencedColumnName = "id") } ) public class Message { // . Tests mit einer Vererbungshierarchie und einer Sekundärtabelle für die Basisklasse waren nicht erfolgreich in Hibernate.. ( not null primary key. oder muss umgekehrt aus mehreren Tabellen zusammengesetzt werden.

count(*) from Message m left outer join PMQ_Message pm on (…) group by m. Java-technisch müsste man das Erzeugen neuer Statistikobjekte noch durch einen entsprechend privaten Konstruktor verbieten. // . updatable=false) protected int numQueues.. in wievielen PMQ's eine Message vorkommt.id. numQueues ) as select m. wie der Update verboten ist. @Column( insertable=false. Um Performance-Vorteile zu nutzen. Arno Schmidhauser November 2006 Seite 61 . create view MessageStatistics ( id. Die Tabelle PMQ_Message ist die Assoziationstabelle zwischen Message und PMQ. indem sowohl das Einfügen. Die Spalten der Klasse sind read only gehalten..JPA – Java Persistence API Mapping von SQL-Views • Komplexe Abfragen werden in einer Datenbank gerne als (materialisierte-) View vorgehalten.id 61 Obiges Beispiel berechnet. Beispiel: @Entity public class MessageStatistics { @Id protected long id. kann eine View wie eine Entity angesprochen werden.

@PostUpdate: Nach der entsprechenden Datenbank-Operation. } @PostLoad @PostUpdate protected void uncompress() { . @PreRemove: Vor der entsprechenden Datenbank-Operation. um Einfluss auf den Lade. @PostRemove: Nach der entsprechenden Datenbank-Operation.oder Speichervorgang von Objekten zu nehmen. Sie können public. remove() und refresh() betroffen sind. nicht zwingend synchron zur remove()-Methode. Unmittelbar vor flush(). Callbacks sind void und haben keinen Übergabeparameter. Unmittelbar nach flush(). @PostPersist: Nach der entsprechenden Datenbank-Operation. die via kaskadierten Aufruf von persist(). • Callbacks werden vom Entwickler definiert.. Unmittelbar nach flush(). Ein generierter Primärschlüssel ist in dieser Methode verfügbar. Unmittelbar vor flush(). Unmittelbar vor flush(). Die Callbacks werden auch für Objekte. Beispiel: @Entity public class ImageMessage extends Message { @PrePersist @PreUpdate protected void compress(){ . Unmittelbar nach flush().. protected oder private sein.JPA – Java Persistence API Callbacks • Callbacks sind eine gängige Methode. ausgeführt.. Arno Schmidhauser November 2006 Seite 62 . } 62 Folgende Callback-Annotations sind spezifiziert: • • • • • • • @PrePersist: Vor der entsprechenden Datenbank-Operation.. nicht zwingend synchron zur persist()-Methode. @PostLoad: Nach implizitem Refresh. oder aufruf der refresh()-Methode. @PreUpdate: Vor der entsprechenden Datenbank-Operation. aber vom JPA Framework aufgerufen.

63 Arno Schmidhauser November 2006 Seite 63 . dass die Löschung der abhängigen Datensätze zuerst erfolgt.JPA – Java Persistence API Foreign Key Constraints • Änderungsoperation von einem Peristenzframework erzeugen oft Konflikte mit Fremdschlüsselbedingungen. • Hibernate arbeitet mit einer sogenannten "Write Behind Queue" : SQL-Befehle werden so geordnet. wenn Datensätze in der falschen Reihenfolge gelöscht werden.

angeboten werden.auto" value="create"/> DDL-Code soll bei Änderungen von Hibernate ausgeführt werden: <property name="hibernate.JPA – Java Persistence API DDL Angaben in Annotations • Die Erzeugung von DDL kann. DDL-Code soll von Hibernate jedesmal neu ausgeführt werden in der Datenbank: <property name="hibernate. nullable=false. aber muss nicht durch eine Implementation der JPA Spez. welche die Erzeugung von DDL-Befehlen ermöglichen.xml die Erstellung von DDL-Code beeinflussen.hbm2ddl..auto" value="update"/> Arno Schmidhauser November 2006 Seite 64 .. // . lenghth=64 ) public BigDecimal sender. sind verschiedene Angaben (Annotations und Attribute davon) vorgesehen. columnDefinition="varchar". Beispiel: public class Message { @Column (unique = false. • In der JPA Spez.hbm2ddl. 64 In Hibernate kann man via folgende Einstellungen in persistence.

JPA – Java Persistence API Aufgaben • Erstellen Sie ein Listenmapping für die Messages einer PMQ und untersuchen Sie.logger.hibernate.logger.type=trace • Erstellen Sie eine eingebettete Klasse. Zum Tracen von SQL-Befehlen das File /etc/log4j. wenn einer Liste eine neue Message angefügt wird. zum Beispiel für den Owner einer PMQ.hibernate.SQL=trace log4j.org. 65 Arno Schmidhauser November 2006 Seite 65 .properties aus dem Hibernate Home in das Applikationsverzeichnis kopieren und anpassen: log4j. welche SQL-Befehle erzeugt werden.org.

JPA – Java Persistence API V Objektverwaltung und Transaktionsmanagement 66 Arno Schmidhauser November 2006 Seite 66 .

jedoch ohne Konsistenz. Auf Objekte kann auch ausserhalb von Transaktionen zugegriffen werden. Selbstverständlich gilt ein Transaktionsmodell: Der Zugriff auf Objekte erfolgt ab Beginn der Transaktion. Der Transfer von Objekten von und zur Datenbank kann manuell erzwungen werden synchron zum Aufruf. 67 Arno Schmidhauser November 2006 Seite 67 . die Synchronisation mit der Datenbank wird spätestens beim Commit abgeschlossen und unterliegt der ACID-Regel.JPA – Java Persistence API Grundsätze Der Transfer von Objekten von und zur Datenbank erfolgt automatisch: so spät wie möglich Lazy Access.und Synchronisationsgarantie.

wurde aber abgekoppelt. Eine Methode zum Feststellen des Objektzustandes exisitiert nicht. – detached Das Objekt hat eine Entsprechung in der Datenbank. 68 Ein Objekt ist new wenn es java-mässig instantiiert. aber noch nicht mit persist() bearbeitet wurde. – removed Das Objekt existiert noch. durch Schliessen des Entity Managers. Der Zustand wird nicht mehr automatisch abgeglichen mit der Datenbank.JPA – Java Persistence API Objektzustand • Objekte haben vier mögliche Zustände: – new Objekt ist neu erzeugt. hat noch keinen Zusammenhang mit der Datenbank und noch keine gültige ID. oder durch Aufruf der clear() Methode des Entity Managers. Der Aufruf von remove() hat für ein new-Objekt keine Auswirkungen. durch Serialisierung. Ein Objekt wird detached nach einem Rollback. Änderungen werden vom Entity Manager automatisch getracked und mit der DB abgeglichen. für ein managedObjekt wird effektiv der Zustand auf remove gesetzt. Arno Schmidhauser November 2006 Seite 68 . Nach dem persist() ist es managed. ist aber zum Löschen markiert. – managed Das Objekt hat eine Entsprechung in der Datenbank. für ein detached-Objekt wird eine IllegalArgumentException geworfen.

2 JPA Spezifikation.JPA – Java Persistence API Fragen zur Objektverwaltung 1. Was passiert mit einem Objekt beim Rollback? 6. Zu welchen Zeitpunkten wird ein geändertes Objekt in die Datenbank zurückgeschrieben? 4. Können persistente Objekte serialisiert und von einer Applikation in eine anderen transportiert werden? 69 Siehe auch Kapitel 3. Ab wann hat ein neues Objekt eine gültige ID (Wenn die ID via Datenbank-Resourcen vergeben wird und nicht manuell)? 2. Zu welchen Zeitpunkten wird der Objektzustand von der Datenbank eingelesen? 3. Arno Schmidhauser November 2006 Seite 69 . In welchem Zustand ist ein Objekt nach dem Commit? Kann es nach der Transaktion weiterverwendet werden? 5.

.out.. @OneToMany( cascade={ CascadeType.getId() ) Java Default 0 ! gültige ID.append( m ).B. // .) pmq. public long getId() { return id. } // . 70 @Entity public class PMQ implements Serializable { @Id protected long id. } Arno Schmidhauser November 2006 Seite 70 . z. Message m = new Message(.getId() ) em.PERSIST } ) protected List<Message> messages = new Vector<Message>(). protected String qname..JPA – Java Persistence API Objekt ID • Ein neues Objekt bekommt erst eine ID. System...flush() System. 1 • Die persist()-Methode ist eine logische Operation.println( m. wenn es das erste Mal physisch in die Datenbank transportiert wird. @Entity public class Message { @Id @GeneratedValue long id.println( m.out..

Für Applikationen. • Eine neue Transaktion führt nicht automatisch zum erneuten Einlesen bestehender Objekte. } Defaultwerte für FetchType: OneToOne EAGER OneToMany LAZY ManyToOne EAGER ManyToMany LAZY Der Gebrauch von refresh() bezüglich Isolationsgrad in der Datenbank muss kritisch betrachtet werden: Bei Isolationsgrad READ_COMMITTED bringt die Methode keine absolute Sicherheit bezüglich Aktualität und Synchronisation mit der Datenbank.LAZY gesetzt ist. @OneToOne. @OneToOne( fetch = FetchType. • Wenn FetchType.refresh()-Methode.JPA – Java Persistence API Einlesen • Der Objektzustand wird beim ersten Zugriff auf das Objekt eingelesen. als Attribut angegeben. die nie terminieren und die periodisch Daten lesen und anzeigen/weitergeben kann die Funktion jedoch nützlich sein.EAGER ) protected Owner owner. • Der Objektzustand wird nie automatisch aufgefrischt. • Wenn FetchType. fetch = FetchType. Arno Schmidhauser November 2006 Seite 71 . 71 Der FetchType wird bei den Annotationen @Basic. } @Entity public class Message { @ManyToMany( mappedBy = "messages". werden referenzierte Objekte beim ersten Gebrauch eingelesen.EAGER gesetzt ist. nur via die EntityManager. OneToMany etc. bei Isolationsgrad REPEATABLE_READ ist refresh() überflüssig.EAGER ) protected List<PMQ> pmqs = new Vector<PMQ>(). zum Beispiel: @Entity public class PMQ { @ManyToMany( fetch = FetchType. werden referenzierte Objekte ebenfalls mitgeladen.EAGER ) protected List<Message> messages = new Vector<Message>().

Arno Schmidhauser November 2006 Seite 72 . • Das Zurückschreiben beinhaltet kein Refresh allfälliger Änderungen in der Datenbank in der Zwischenzeit.commit() m.setContent("A") tx.flush() Zustand von m in Appl in DB A A A C B C SQL-Client update Message set content = 'B'. • Das Zurückschreiben betrifft nur Änderungen.JPA – Java Persistence API Zurückschreiben • Das Zurückschreiben findet zum Commit-Zeitpunkt oder explizit mit der flush() Methode statt. commit 72 Bei Hibernate findet der Update so fein wie möglich statt. bei der Änderung eines Feldes eines Objektes. taucht nur dieses Feld im SQL-Update-Befehl auf. nicht ungeänderte Daten. Applikation m.setContent("C") em.

3. • Wenn der Persistence Context TRANSACTION ist. Das Objekt wird nach folgenden Regeln eingekoppelt: • • • existiert ein Objekt mit derselben ID wird der Zustand des detached Objekt übernommen.1 Wurde das Objekt in der Datenbank zwischen dem Detach und dem Merge geändert sind zwei Resultate möglich beim Commit der Transaktion: • • Das Objekt enthält eine Versionsnummer (@Version-Annotation) zur Versionenkontrolle. Weitere Fälle siehe Kap. In diesem Fall wird ein Fehler ausgegeben.JPA – Java Persistence API Objektzustand nach dem Commit.merge() innerhalb der nächsten Transaktion wieder eingekoppelt werden. Es kann als eine Art Transfer-Objekt angesehen werden. geht ein Objekt in den Zustand detached über nach dem Commit. Die EntityManager.merge()-Operation erlaubt das Einkoppeln des Objektes in eine Datenbank beim Zielsystem. Es kann serialisiert von einer Applikation in eine andere transportiert werden. exisitiert die ID nicht. bleibt ein Objekt im Zustand managed nach dem Commit. Das Objekt enthält keine Versionsnummer. dann wird der Zustand des gemergeden Objektes in die DB zurückgeschrieben. – Änderungen müssen mit EntityManager. Ein Objekt im Zustand detached ist vom EntityManager abgekoppelt. 73 Der Persistence Context TRANSACTION ist der Normalfall bei Container Managed Entities. Es entsteht somit ein LostUpdate. Weiterverwendung des Objektes • Wenn der Persistence Context EXTENDED ist.4. Arno Schmidhauser November 2006 Seite 73 . wird ein neues Objekt mit einer neuen ID angelegt und der Zustand des detached Objektes übernommen. – Änderungen nach dem Commit werden aufbewahrt und im Rahmen der nächsten Transaktion in die Datenbank übernommen.2. Der Persistence Context EXTENDED ist der Normalfall bei Java Standard Applications.

getReference(). Objekte via Methoden des Entity Manager neu laden: find(). 74 Arno Schmidhauser November 2006 Seite 74 .JPA – Java Persistence API Objektzustand nach dem Rollback • Nach einem Rollback ist jedes noch vorhandene Objekt im Zustand detached. createQuery() usw. jedoch der Zustand in der Datenbank. Mögliche Inkonsistenzen. Die Belegung der Felder wird durch den Rollback nicht geändert.

EAGER deklariert sind. ObjectOutputStream os = new ObjectOutputStream( fos ). wenn alle Beziehungen mit fetch=FetchType.4) nur.2.close(). wenn die zu serialisierenden Objekte bereits im Cache (Persistence Context) sind. os. os. FileOutputStream fos = new FileOutputStream( "myfile. Auf logischer Ebene sichergestellt ist dies gemäss EJB Spezifikation (Kap 3. Arno Schmidhauser November 2006 Seite 75 .JPA – Java Persistence API Serialisierung und Transport • Objekte können zwecks Weitergabe in andere Persistenzkontexte oder Applikationen serialisert werden.ser" ). 75 Für den Serialisierungvorgang wird ein Objekt nicht von der Datenbank geladen. Die Serialisierung funktioniert als nur.writeObject( pmq ).

writeObject( pmq ).ser" ).readObject(). … Arno Schmidhauser November 2006 Seite 76 .EAGER ) protected List<Message> messages = new Vector<Message>().commit(). Iterator<Message> it = pmq. … Die serialisierten Objekte sind unabhängig vom verwendeten Persistenz-Framework (Hibernate).iterator(). Damit der Merge kaskadiert arbeitet. while( it.getAll().JPA – Java Persistence API Serialisierung eines Objektes: FileOutputStream fos = new FileOutputStream( "myfile. PMQ pmq = (PMQ) is. tx.println( m. und können beispielsweise wie folgt benützt werden: //deserialize pmq and related ojects FileInputStream fis = new FileInputStream( args[0] + ". os.ser" ). tx.createEntityManager(). emf = Persistence.ser" ). PMQ pmq = (PMQ) is.MERGE } ) protected List<Message> messages = new Vector<Message>(). muss der entsprechende CascadeType angegeben werden: @Entity public class PMQ implements Serializable { @ManyToMany( cascade={ CascadeType. is. Die zu serialisierenden Objekte müssen im Cache vorhanden sein. tx = em. ObjectOutputStream os = new ObjectOutputStream( fos ). System. os. FileInputStream fis = new FileInputStream( args[0] + ". Ein geeigneter FetchType ist zu wählen: @Entity public class PMQ implements Serializable { @ManyToMany( fetch=FetchType.hasNext() ) { Message m = it.close().close().begin().createEntityManagerFactory("PMQ").toString() ). ObjectInputStream is = new ObjectInputStream( fis ). mit Hilfe der merge() Operation. is. fetch=FetchType. ObjectInputStream is = new ObjectInputStream( fis ).EAGER ) protected List<PMQ> pmqs = new Vector<PMQ>(). em = emf.getTransaction().close().readObject().next().out.EAGER ) protected Owner owner.merge( pmq ). em. @OneToOne( fetch=FetchType. } Die serialisierten Objekte können auch wieder in die Datenbank eingekoppelt (abgeglichen oder neu erzeugt) werden. … @Entity public abstract class Message implements Serializable { @ManyToMany( mappedBy = "messages".

Ein Objekt wird geändert: Zu welchem Zeitpunkt wird es in der Datenbank gesperrt? 3.JPA – Java Persistence API Fragen zum Transaktionsmanagement 1. währenddem die Transaktion läuft (Mit welchem Isolationsgrad wird gearbeitet)? 2.4 JPA Spezifikation. ob Daten beim Commit in der Zwischenzeit von anderen Prozessen geändert wurden auf der DB? 77 Siehe auch Kapitel 3. Kann entdeckt werden. Ist der Objektzustand nach dem Lesen in die Applikation in der Datenbank eingefroren. Arno Schmidhauser November 2006 Seite 77 .

Hibernate erlaubt jedoch den Durchgriff auf die JDBC-Ebene und damit.createEntityManager(). der keine langen Lesesperren hält.hibernate. 78 Die Spezifikation geht von einem Isolation Level 1 (READ COMMITTED) aus. con.hibernate.Connection.getSession().connection. Gelesene Daten sind während der Transaktion nicht eingefroren.JPA – Java Persistence API Isolationsgrad • JPA arbeitet standardmässig im Isolationsgrad READ COMMITTED.connection().EntityManagerImpl. tx.ejb. import org. //… tx.setTransactionIsolation( Connection. respektive einem anderen Isolationsgrad.begin() Connection con = ((EntityManagerImpl)em). emf = Persistence. In Hibernate kann der Isolationsgrad entweder entweder via persistence. Die Konsistenzprobleme Lost Update und Phantom können auftreten.Session. // create EntityManager em = emf. // Create EntityManagerFactory for a persistence unit called pmq.TRANSACTION_SERIALIZABLE ).createEntityManagerFactory("PMQ"). Das Setzen von strengeren Isolationsgraden über die EJB-Spez.xml eingestellt werden: <property name="hibernate. import org. ist nicht möglich.commit() Arno Schmidhauser November 2006 Seite 78 .sql. abgesehen vom Abholen der JDBC-Connection.isolation" value="8" /> oder via Anzapfen der JDBC-Session: import java. einen im Java-Umfeld standardisierten Zugriff auf Transaktionsbefehle.

JPA – Java Persistence API Sperrzeitpunkt • Daten werden in der Datenbank im Rahmen von SQLBefehlen gesperrt. Integritätsverletzungen treten effektiv erst zum Commitzeitpunkt auf. 79 Arno Schmidhauser November 2006 Seite 79 . finden das Sperren erst beim Commit statt. Deadlocks. • Da Änderungen vorerst lokal durchgeführt und erst zum Commitzeitpunkt in die DB propagiert werden. Allfällige Wartesituationen.

In Hibernate ist der lock()-Befehl sehr nützlich. Die Spezifikation verlangt lediglich. sowie. danach in der Applikation ausführlich bearbeitet werden. Damit ist ein pessimistisches Verfahren synchron zum Applikationsablauf möglich. sie kann grundsätzlich einfach mit Versionierung arbeiten und die Konfliktprüfung damit auf den commit-Zeitpunkt verschieben.JPA – Java Persistence API Explizites Locking • Der Entity Manager stellt einen expliziten lock() Befehl zur Verfügung.WRITE. der Versionenzähler erhöht wird. ist jedoch sehr unverbindlich definiert.lock( pmq. Arno Schmidhauser November 2006 Seite 80 . • Beispiel: em. Die Arbeitsweise des lock()-Befehles ist sehr schwammig definiert in der JPA Spez. Die Implementation muss nicht unbedingt synchron mit dem Aufruf von lock() einen Lock setzen. Für nicht versionierte Objekte besteht von der Spezifikation her kein Unterschied zwischen LockModeType. dass ein Lost Update für das gelockte Objekt verhindert wird.WRITE ) 80 Explizites Locking ist mit EntityManager. LockModeType.READ und LockModeType.WRITE ist von der Spezifikation lediglich verlangt. • Das Verhalten ist stark implementationsabhängig. Daten können lesend gesperrt werden. wenn mit Versionierung gearbeitet wird. ohne dass die Gefahr zwischenzeitlicher Änderungen durch andere Prozesse besteht.lock( ) möglich. dass mit LockModeType. Für LockModeType.READ ein Lost Update für das gelockte Objekt verhindert wird. weil unmittelbar gelockt wird.

WRITE läuft ebenfalls über den Primärschlüssel und das Versionenattribut: update PMQ set version=? where id=? and version=? Bei diesem Update-Befehl wird gleichzeitig die Versionsnummer um 1 erhöht. Mit obigem SQL-Befehl sind auch Deadlocks ausgeschlossen. Der SQLBefehl zur Realisierung von LockModeType. sondern es geht nur darum. Arno Schmidhauser November 2006 Seite 81 . rowlock) where id =? and version =? Der SQL-Befehl verwendet den Primärschlüssel und das Versionenattribut (dieses ist deshalb notwendig bei Hibernate. muss sie von Hibernate jeweils für einen bestimmten SQLDialekt generiert werden. Hibernate kennt den SQL-Dialekt aus dem Konfigurationsfile persistence. weil nicht nur ein Read-Lock sondern einen Update-Lock (updlock) gesetzt wird.xml.READ in einen SQL-Befehl umsetzt: select id from PMQ with (updlock. rowlock) produktspezifisch ist.JPA – Java Persistence API Beispiel. Da die Klausel with (updlock. ansonsten arbeitet das Locking nicht). Der Befehl wurde von Hibernate spezifisch für SQL-Server 2005 generiert. mit dem select-Befehl die Sperre auszulösen. wie Hibernate LockModeType. Der gelesene Primärschlüssel wird nicht wirklich verwendet.

wenn ein Versionenfeld definiert ist. Beim Zurückschreiben wird der Wert im Objekt mit dem Wert in der Datenbank verglichen.sql. welche mit mehreren. Der Begriff "Version" ist leicht missverständlich. Mit der @Version-Annotation wird für diese Entity das Versioning automatisch eingeschaltet..Timestamp in Frage. da nur diese garantiert monoton aufsteigend sind. aktuellen Version verwaltet wird. verpflichten sich andere Applikationen. short. Die Versionierung ist ein sehr einfaches applikatorisches Verfahren. Es können gleichzeitig versionierte und nicht-versionierte Entities existieren. create table "PMQ" ( version bigint not null.. 82 Die Versionennummer wird beim Lesen von Objekten mitgenommen. Stürzt die besitzende Applikation ab. sonst sind Lesevorgänge technisch blockiert. den Datensatz nur lesend oder gar nicht in Gebrauch zu nehmen. Dies im Gegensatz zu einigen Datenbanksystemen. vollständigen Versionen desselben Datensatzes arbeiten können. Sind die beiden nicht gleich. Ein anderes applikatorisches Verfahren. Ist diese Marke gesetzt.. die Marke kann um Informationen über den Benützer und den Zeitpunkt des Checkout ergänzt werden. long. dass der Datensatz in Gebrauch ist.. Das Verfahren ist sehr flexibel. dass Konflikte verhindert. Arno Schmidhauser November 2006 Seite 82 . Die Zahlentypen sind vorzuziehen. • Mit einer Versionsnummer können Lost Updates detektiert und vermieden werden: @Entity public class PMQ { @Version protected long version. Integer. setzt sie gleichzeitig für eine Marke. Nach dem Checkout muss sofort ein Commit durchgeführt werden. Long. // . Short und java. Als Datentyp für die Versionierung kommen int. das mögliche Konflikte beim Zurückschreiben detektiert. sondern nur die Nummer einer einzigen. Der Versionierungsmechanismus und die Versionsprüfung tritt automatisch in Kraft. // . ist das Checkout/Checkin: Liest eine Applikation einen Datensatz. weil nicht mehrere Zustandsversionen. Nachteile: Jedes Lesen erfordert auch einen Schreibvorgang (Setzen der Marke).JPA – Java Persistence API JPA Versionierung • Die Versionierung im Rahmen von JPA ist als applikatorisches Locking zu verstehen. Mit der Versionierung kann ein Concurrency Control über Transaktion hinweg realisiert werden. wird die Transaktion zurückgesetzt und eine OptimisticLockException ausgeworfen. Das Versionierungsattribut darf nicht durch die Applikation geändert werden. kommt es nie zu einem Checkin.

83 Arno Schmidhauser November 2006 Seite 83 . dass Producer und mehrere Consumer gleichzeitig laufen können.JPA – Java Persistence API Aufgaben • Entwickeln Sie die Producer und Consumer-Klasse für eine Message Queue so. Es muss ein korrekter Ablauf im Sinne der Queue Semantik garantiert sein: Jede Message wird nur einmal gelesen und angezeigt.

JPA – Java Persistence API VI JPA Query Language 84 Der offizielle Name ist Java Persistence API Query Language. Der Name EJB QL ist für EJB Version 2 reserviert. Arno Schmidhauser November 2006 Seite 84 .

Abfragen operieren auf dem Klassenmodell aller Entitäten.und Delete-Operationen – OUTER |INNER JOIN. die zu keiner PMQ gehören. aber diese sind in der Query Language nicht verwendbar). Will man beispielsweise alle Messages finden. nicht dem SQL-Datenmodell. Abfrage bei unidirektionaler Beziehung von PMQ zu Message: select m from Message m where m not in ( select m2 from PMQ q JOIN q. GROUP BY. Auf das EJB-Modell ausgerichtete Abfragesprache zum Suchen und Bearbeiten von Entities. so sind die Abfragen unterschiedlich.messages m2 ) Abfrage bei bidirektionaler Beziehung zwischen PMQ und Message: select m from Message m where m. "SQL-Mapper". Gegenüber EJB QL 2.JPA – Java Persistence API Konzept 1. Ausdrücke in der Select-Klausel) 85 Zu Punkt 2: Im Objektmodell gibt es keine Fremdschlüssel (in der DB schon. HAVING Klauseln – Projektionen (Abfrage einzelner Attribute resp. 3. die im Klassenmodell vorhanden sind.pmq is null Arno Schmidhauser November 2006 Seite 85 . je nachdem ob zwischen PMQ und Message eine unidirektionale oder eine bidirektionale Beziehung definiert wurde (das relationale Modell ist für beide Fälle dasselbe). Es können also nur Felder/Beziehungsattribute verwendet werden. 2.1 wesentliche Erweiterungen: – direkte Update.

Sie übernimmt einen Query Text in der JPA Query Language (JPA QL). In der zweiten Form kann im Java Source Code in Form einer Annotation ein Named Query definiert werden.JPA – Java Persistence API Query erzeugen • Ausgangspunkt für Abfragen ist der Entity Manager mit den Funktionen: Query createQuery( String query ) Query createNamedQuery( String qryname ) Query createNativeQuery( String sql. mit definiertem Mapping auf Felder einer Klasse: createNativeQuery( "select id. entities={ @EntityResult( entityClass=pmq. column="receiver" ) } )} ) Arno Schmidhauser November 2006 Seite 86 . @FieldResult( field="receiver". @FieldResult( field="sender". fields={ @FieldResult( field="id". receiver from Message". sender.pmqs is empty " ) Auf dieses Query kann mit createNamedQuery( "orphans" ) Bezug genommen werden. "myMapping" ) @SqlResultSetMapping( name="myMapping". Die dritte Form ist für die Ausführung von direktem SQL. column="sender" ). column="id" ).Message. String mapname ) 86 Die erste Funktion ist sicherlich die häufigste Form.class. Anwendung: Wenn dasselbe Query häufig gebraucht wird. query=" select m from Message m where m. beispielsweise: @NamedQuery( name="orphans".

createQuery( "select m from Message m where m.sender = 'Rolf'") • Parametrisierte Queries Query q = em.sender = ?1") 87 Arno Schmidhauser November 2006 Seite 87 . Beispiele • Unparametrisiertes Query Query q = em.JPA – Java Persistence API Query.createQuery( "select m from Message m where m.sender = :sender") Query q = em.createQuery( "select m from Message m where m.

setParameter( "sender". Arno Schmidhauser November 2006 Seite 88 .setParameter( 1. Date value. "Rolf" ).setParameter( string name. TemporalType temporalType) Auch ganze Objekte dürfen Parameter sein. welche Teile eines Datums-/Zeitwertes effektiv im Query verwendet werden sollen.setParameter( int pos. "Rolf" ).JPA – Java Persistence API Query. Object value ) Beispiel q. um festzulegen. TemporalType temporalType) setParameter(String name. • Übergabe eines Positionsparameters Query. 88 Für die Übergabe von Datums. setParameter(int pos. Calendar value. TemporalType temporalType) setParameter(String name. Parameterübergabe • Übergabe eines Namensparameters Query. TemporalType temporalType) setParameter(int pos. Calendar value. Date value.und Zeitwerten existieren noch vier weitere Methoden. Object value ) Beispiel q.

Wenn der FlushModeType auf AUTO gesetzt ist. wenn vor der Query-Ausführung neue persistente Objekte erzeugt. delete) durchgeführt werden. Erzeugt eine Exception. Der FlushModeType ist wichtig. findet vor der Ausführung eine Synchronisation (flush()) zwischen dem Cache und der Datenbank statt. Ausführung • Einschränken der Resultatmenge: setFirstResult( int pos ) setMaxResults( int max ) • Abholen des Resultates List getResultList() Object getSingleResult() int executeUpdate() • Synchronisation setFlushModeType( FlushModeType type ) 89 Die Methoden zum Einschränken der Resultatmenge können unter Umständen performance-kritisch sein. findet keine Synchronisation statt. Enthält die Abfrage atomare Datentypen. Wenn der FlushModeType auf COMMIT gesetzt ist.util. gelöscht oder modifiziert wurden.. Für nicht sortierte Abfragen ist eine Liste nicht störend. Eine Liste ist für sortierte Abfragen der einzig korrekte Datentyp. Einfachheitshalber wurde deshalb auf eine allgemeine Collection als Rückgabetyp verzichtet. beispielsweise in "select m. wenn kein oder mehr als ein Resultat entsteht. wird eine Liste von Arrays zurückgegeben. welche bereits datenbankseitig zum Eliminieren der nicht gewünschten Objekte führen. weil das Query gegen die Datenbank abgesetzt wird. und das Query wird gegen den momentan in der Datenbank existierenden Zustand ausgeführt. ) vertsteht.priority from Message m" werden die atomaren Typen in die entsprechenden Objekttypen umgewandelt.List abgeholt. ohne Berücksichtigung noch pendenter Änderungen der laufenden Transaktion. Enthält die select-Klausel mehrere Werte oder Objekte. Mit executeUpdate() können modifizierende Abfragen (update. Es ist abzuklären. Arno Schmidhauser November 2006 Seite 89 .. ob die zugrundeliegende Datenbank entsprechende SQL-Ergänzungen (beispielsweise select top .JPA – Java Persistence API Query. dass nur ein einziges Resultat liefert. Im Regelfall werden die Objekte mit getResultList als java. Mit getSingeResult() wird ein Abfrageresultat abgeholt.

toString() ).JPA – Java Persistence API Query.iterator(). } } 90 Mit obigem Code-Stück kann jede Art von Query-Resultat abgeholt werden.next(). Arno Schmidhauser November 2006 Seite 90 .out. } else{ System. while( itm.createQuery( qrystring ).toString() ).getResultList().out.hasNext() ) { Object o = itm. List result = qry.println( x. Resultate abholen Query qry = em. if ( o instanceof Object[] ) { for( Object x : (Object[])o ) System. Iterator itm = result.println( o.

(). ein Aggregatausdruck oder ein Wertausdruck oder eine Kombination der drei letzteren. Die Groupklausel. [NOT] IN. >. dass keine der Klauseln Methodenaufrufe auf den Objekten erlaubt. wird das Kreuzprodukt gebildet wie in SQL. <> (not equal). [NOT] EXISTS. Beide beziehen sich auf Pfadausdrücke. Die Whereklausel kennt die üblichen Vergleiche und Operatoren: =. Andere OO-Abfragensprachen. [NOT] LIKE. die Havingklausel und die Orderklausel arbeiten sinngemäss wie in SQL. ein Pfadausdruck. Beispiel: from PMQ q . [NOT] BETWEEN. NOT. >=. OR. In der Whereklausel sind die speziellen Prädikate IS [NOT] EMPTY und [NOT] MEMBER OF zu erwähnen. Zu erwähnen ist. Die Fromklausel enthält die Grundmengen an Objekten mit einem Referenznamen (der in JPA QL obligatorisch ist) . IS [NOT] NULL. Aufbau • Abfragen in JPA QL folgen demselben Befehlsaufbau wie SQL: select selectklausel from fromklausel [ group by groupklausel ] [ having havingklausel ] [ where whereklausel ] [ order by orderklausel ] 91 In der Selectklausel kann eine Objektreferenz stehen (die in der Fromklausel definiert wird).JPA – Java Persistence API Abfragen. <. Insbesondere sind auch Pfadausdrücke erlaubt und die Orderklausel erlaubt die Zusätze ASC und DESC für die Sortierung. z. Werden mehrere Grundmengen angegeben ohne Join-Bedingung. AND. Arno Schmidhauser November 2006 Seite 91 . <=. die in Collections enden. JDO Query Language erlauben dies.B.

Wenn es zugelassen wäre. Auch wenn das Ojekt owner privat ist. ob sender ein Feld der Collection an sich. Ein Pfadausdruck ermöglicht die direkte Navigation von einem äusseren zu inneren. ist vorallem für die Vewendung in der from. kann darauf navigiert werden. so fällt die entsprechende Zeile aus dem Resultat weg. insbesondere. Bei genauerer Überlegung ist jedoch klar.owner. Ein Pfadausdruck. oder ein Feld der darin eingebetten Elemente ist.messages from PMQ q 3.messages.JPA – Java Persistence API Pfadausdrücke 1. dass das Konstrukt falsch ist. ist die Frage.owner from PMQ q select q. Ein Pfadausdruck kann in einer Collection enden: select q. zu 3: Der Versuch. der in einer Collection endet. weil er beispielsweise in Hibernate zugelassen ist. Es würde also eine Mehrdeutigkeit auftreten. der in einer Collection endet und in einer select-Klausel steht. Aus diesen Gründen wird auf dieses Konstrukt verzichtet in objektorientierten Abfragesprachen (Nicht hingegen im XML-Umfeld: XPath und XQuery bauen genau auf diesen Konstrukten auf). ist von der JPA Spez. solche Ausdrücke zu verwenden.name from PMQ q 2. Damit wiederum kommen Probleme mit im Kreis herum referenzierten Objekten ins Spiel. Er soll aber hier als Illustration dienen. zu 2: Ein Pfadausdruck. Im Weiteren wäre zu bedenken.sender from PMQ q 92 Zu 1: der Ausdruck nimmt keine Rücksicht auf die Kapselung der inneren Objekte. Ist ein Objekt im Pfad null.oder der where-Klausel vorgesehen. ist ein häufiger Fehler. Arno Schmidhauser November 2006 Seite 92 . eigentlich nicht erlaubt. dass sender wieder eine Collection sein könnte. referenzierten Objekten: select q. Ein Pfadausdruck kann nicht über eine Collection hinweg navigieren: select q.

sender from PMQ q left join q.messages m 93 Im ersten Beispiel sind Message-Objekte gesucht. Zu beachten das Schlüsselwort distinct. Im zweiten Beispiel sind PMQ-Objekte gesucht. Arno Schmidhauser November 2006 Seite 93 .qname. Im drittten Beispiel ist die Möglichkeit eines Outer Joins illustriert.JPA – Java Persistence API Join-Abfragen • Ein Join von zwei Grundmengen bedingt eine definierte Beziehung in den beiden Klassen.priority = 1 Name jeder PMQ mit Absender aller Messages select q.messages m where m.qname = 'q1' Alle PMQs mit Messages der Priorität 1 select distinct q from PMQ q join q. damit jede PMQ nur einmal in das Resultat kommt. Der rechte Teil der Join-Operation entspricht einer SQL-Join-Bedingung zwischen PMQ und Message. m. Beispiele: Alle Messages der PMQ q1 select m from PMQ q join q.messages m where q.

owner.priority = 1 ) • Suche alle Messages.name = 'Rolf' ) 94 Arno Schmidhauser November 2006 Seite 94 . die zu einer PMQ von Rolf gehören select m from Message m where m in ( select m from PMQ q JOIN q.messages where q.JPA – Java Persistence API in und exists • Die Operatoren IN und EXISTS sind ähnlich wie in SQL. in der Unterabfrage können auch Pfadausdrücke verwendet werden: • Suche alle Queues mit Meldungen der Priorität 1 select q from PMQ q where exists ( select m from q.messages m where m.

.messages m where m = ?1 Arno Schmidhauser November 2006 Seite 95 .messages IS EMPTY 2. m ). qry. // irgendwoher Query qry = em. member of • is empty und member of ergeben gut verständliche Abfragen auf Pfadausdrücken.setParameter( 1.getResultList().JPA – Java Persistence API is empty. List result = qry.createQuery( "select q from PMQ q where ?1 member of q. Suche PMQ's in denen die Message m vorkommt Message m = . die in Collections enden: 1.messages ) Beispiel 3 könnte man ersetzen durch select distinct q from PMQ q join q. 95 Grundsätzlich sind die beiden Operatoren nicht zwingend notwendig: Beispiel 1 könnte man ersetzen durch select q from PMQ q where not exists ( select m from q.messages ) Beispiel 2 könnte man ersetzen durch select distinct q from PMQ q join q.messages m oder select q from PMQ q where exists ( select m from q.messages" ).. Suche nicht-leere PMQs select q from PMQ q WHERE q. Suche leere PMQs select q from PMQ q WHERE q.messages IS NOT EMPTY 3. .

existiert aber nicht. m.content from Message m. SimpleMessage s where s.JPA – Java Persistence API Abfragen in Vererbungshierarchien • Alle Objekte einer Klasse und ihrer Unterklassen: select m from Message m • Alle XMLMessages in einer PMQ: select xm from XmlMessage xm where xm in ( select m from PMQ q JOIN q.qname = 'q1' ) • Alle Messages.id Arno Schmidhauser November 2006 Seite 96 .receiver.sender. die konkret SimpleMessages sind: select m from Message m where m in ( select mm from SimpleMessage mm ) 96 Ein instanceof Operator wäre nützlich. s. Folgende Abfrage für das Auffinden von Attributen aus SimpleMessage ist möglich: select m.id = m.messages m where q.

Dadurch entsteht eine nicht ganz klare Semantik: Bedeutet der Vergleich q.messages.id and ( m1.id and pmq.JPA – Java Persistence API Erstaunliches .id. oder nur einer? Im vorliegenden Fall wird der Gleichheitsoperator offenbar als 'mindestens ein' interpretiert..sender = 'bob'. from PMQ pmq. Message m1. PMQ_Message pm2. dass eine Navigation über Collections hinweg (q. • Die folgenden Abfragen liefern unter Hibernate (JBoss) ein gültiges Resultat: select distinct q from PMQ q where q. PMQ_Message pm1.PMQ_id and pm2.sender) möglich ist..messages. Die Navigation über Collections hinweg ist vom Standard nicht vorgesehen.messages_id=m2.sender = 'Alice' 97 Erstaunlich ist an den beiden Abfragen.PMQ_id and pm1. dass alle bob heissen müssen.messages.messages_id=m3.messages.sender='Bob' ) Arno Schmidhauser November 2006 Seite 97 .messages.id=pm2.sender='Alice' or m2.id=pm1. Message m2 where pmq.messages.sender = 'Bob' or q. Die zweite der obigen Abfragen wird vom Framework in folgenden Join umgewandelt: select distinct pmq.sender = 'Alice' select distinct q from PMQ q where q.sender = 'Bob' and q.

JPA – Java Persistence API Fetch Join • Der Fetch Join erlaubt das Abfragen von Objekten. dass die Abfrage eventuell mehrfach dasselbe Objekt zurückliefert. Zu beachten: Die Join-Semantik hat natürlich zur Folge.qname = 'q1' 98 Der Fetch Join kann sowohl inner wie outer sein. select q from PMQ q left outer join fetch q. • Der Fetch-Join ist ein reines Optimierungs-Hilfsmittel. Arno Schmidhauser November 2006 Seite 98 .messages where q. Ist dies nicht erwünscht. mit gleichzeitigem Laden assoziierter Objekte. kann mit select distinct gearbeitet werden. • Der rechte Operand des Fetch Join hat keine Identifikationsvariable und darf in der Abfrage nicht weiter verwendet werden. • Der Fetch Join ist Teil der JPA Spez.

• Beispiel: delete from PMQ q where q.JPA – Java Persistence API Update und Delete • Gemäss Spezifikation können auch Update und DeleteOperationen als "Abfragen" durchgeführt werden. • Update. 99 Arno Schmidhauser November 2006 Seite 99 .und Delete Befehle führen nicht automatisch eine Synchronisation zwischen Datenbank und Applikationscontext (Cache) aus.qname = 'q1' • die Löschung beinhaltet kaskadierte Löschungen. die im Rahmen von Beziehungen definiert sind. Der Hintergrund dürften Performance-Überlegungen sein.

aber in SQL grundsätzlich möglich wären? 100 Arno Schmidhauser November 2006 Seite 100 .JPA – Java Persistence API Aufgaben • Erstellen Sie ein Abfrage-Applikation. die Schwierigkeiten bereiten. um die Möglichkeiten von JPA Query Language auszuloten. Testen Sie mit: • Abfragen von Objekten mit Bedingungen / Sortierung • Abfragen in der Vererbungshierarchie • Gibt es Abfragen.

Sign up to vote on this title
UsefulNot useful