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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

JDO ist ein sehr ambitiöser Ansatz. Komposition abbildbar ↑ Transitive Persistenz oder Persistence by Reachabiltiy • Lazy Fetching • Automatic Dirty Checking • Datenbank-Roundtrips minimieren. Das JPA ist ein Mix der Erfahrungen aus dem EJB 2. Aggregation. den Source Code 100% von Mapping und Verhaltensangaben frei zu halten. 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. mit dem Anspruch.x Technologien. Arno Schmidhauser November 2006 Seite 20 .x Ansatz und dem JDO Konzept.

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

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

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

ob das Mapping in ein Datenbankattribut als DATE.JPA – Java Persistence API Entity. // . } Zahlreiche weitere Mappingangabe.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. sind möglich.. protected String sender. Mit folgenden Annotations kann man andere Mappings vornehmen: @Entity @Table( name="MyMessageTable" ) public class Message { // . protected int priority. z. Arno Schmidhauser November 2006 Seite 24 . @Temporal( TemporalType. z. @Column( name="senderName" ) protected String sender. Datenbank.. public Message() {} // get.B. Beispiel @Entity public class Message { @Id protected long id.TIMESTAMP ) protected Date date.1 JPA Spez. TIME oder TIMESTAMP verstanden werden soll. Für gewisse Datentypen kann man Präzisierungen anbringen. Mit @Temporal wird angegeben..und Schema-Name bei der Tabelle. @Temporal für die Java Typen Date und Calendar. Siehe Kapitel 9. protected String receiver.

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

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

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

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

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

EXTENDED) public class MessageProducer { // .... // . EntityManagerFactory emf = Persistence. Transaction tx = em.createEntityManagerFactory("PMQ").. EntityManager em = emf.createEntityManager(). 32 Arno Schmidhauser November 2006 Seite 32 .getTransaction().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.

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

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

em. em.remove( pmq ). ob die persist() Methode vor oder nach der append()-Methode aufgerufen wird! 35 Arno Schmidhauser November 2006 Seite 35 ... m wird automatisch persistent // . 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. m wird ebenfalls aus der db gelöscht • Es spielt keine Rolle. PMQ pmq = new PMQ( "Dringendes" ).append( m )..persist( pmq ). ) pmq.

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

parseLong(1) ).id = 1 ).. tx. Message m = em. Query qry = em. Die Methode find() lädt auf jeden Fall die unmittelbaren Felder des gesuchten Objektes.. Message m = em. Suchen mit einer Abfrage Objekte werden via eine Abfrage gesucht. // oder . Das Resultat wird als Liste zurückgegeben. 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.JPA – Java Persistence API Aufsuchen persistenter Objekte // .find( Message. Arno Schmidhauser November 2006 Seite 37 ..getReference(Message. tx.. die Methode getReference() erzeugt eine Exception.createQuery( "select m from Message m where m. Long. Iterator it = list. Die Methode find() liefert null. Long. List list = qry. Dies deshalb... // oder . wenn die select-Klausel mehrere Objekte enthält. // .. 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). das heisst eine Liste von Arrays zurückgeliefert wird. Ein Array von Typvariablen ist in Java nicht erlaubt.commit(). wenn das Objekt nicht existiert... Die resultierende Liste hat keinen Typparameter.class..iterator(). weil der Resultattyp vom Query abhängt und dieses in jedem Fall dynamisch ausgewertet wird.parseLong(1)).begin().getResultList().class. Im Weiteren ergäben sich Schwierigkeiten.

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

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 One. Komposition • Der korrekte Unterhalt der Beziehungen ist Sache des Programmes! 40 Der Unterhalt der Beziehungen obliegt dem Programm. 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 . bidirektional One to One. und sehr oft sind Details zum O/R-Mapping und zum Verhalten notwendig. • Folgende Beziehungs-Charakteristiken spielen eine Rolle: – – – – Unidirektional. muss das Programm selber in der Message die Referenz auf die MessageQueue nachführen. One to Many. Sie müssen jedoch deklariert werden.JPA – Java Persistence API Beziehungen • Beziehungen zwischen Entities sind prinzipiell gegeben durch entsprechende Referenzen oder Collection-Member in den Entity-Klassen. Many to Many Vererbung Aggregation. Das Persistenz-Framework tut das nicht. respektive dem Entwickler.

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

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

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

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

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

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

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

• Alle Klassen in der Vererbungshierachie müssen den Primärschlüssel der Basisklasse verwenden (erben). • 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 .JPA – Java Persistence API Vererbung. • Klassen können abstrakt oder konkret sein. Grundsätzliches • Vererbungshierarchien können problemlos verwendet und abgebildet werden.

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

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.

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

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

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

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

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

werden alle Beziehungsdatensätze beim Zurückschreiben in die Datenbank gelöscht und neu eingefügt. position ). unique ( messages_id ). 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.JPA – Java Persistence API Die Abbildung von Listen tendiert zu Performance-Problemen. wird beim Anfügen eines neuen Elementes auch nur der neue Datensatz in der Tabelle eingefügt. Ist die @IndexColumn definiert. Wird beispielsweise keine @IndexColumn definiert in Hibernate. not null. primary key ( PMQ_id. 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 . not null. 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.

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

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

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

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

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

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

dass die Löschung der abhängigen Datensätze zuerst erfolgt. 63 Arno Schmidhauser November 2006 Seite 63 . wenn Datensätze in der falschen Reihenfolge gelöscht werden.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.

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

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

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

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

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

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

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

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

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

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

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. Die Belegung der Felder wird durch den Rollback nicht geändert. jedoch der Zustand in der Datenbank. Mögliche Inkonsistenzen. createQuery() usw. getReference().

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

die zu einer PMQ von Rolf gehören select m from Message m where m in ( select m from PMQ q JOIN q.JPA – Java Persistence API in und exists • Die Operatoren IN und EXISTS sind ähnlich wie in SQL.messages where q. 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.name = 'Rolf' ) 94 Arno Schmidhauser November 2006 Seite 94 .priority = 1 ) • Suche alle Messages.owner.

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

receiver. 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.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.content from Message m.id Arno Schmidhauser November 2006 Seite 96 . existiert aber nicht. SimpleMessage s where s. m.qname = 'q1' ) • Alle Messages.messages m where q. Folgende Abfrage für das Auffinden von Attributen aus SimpleMessage ist möglich: select m.sender.id = m.

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

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

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

die Schwierigkeiten bereiten. um die Möglichkeiten von JPA Query Language auszuloten.JPA – Java Persistence API Aufgaben • Erstellen Sie ein Abfrage-Applikation. aber in SQL grundsätzlich möglich wären? 100 Arno Schmidhauser November 2006 Seite 100 . 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