Sie sind auf Seite 1von 100

Java Persistence API

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

Dieses Skript sttzt 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)

Arno Schmidhauser

November 2006

Seite 2

JPA Java Persistence API

I Wichtige Java Konstrukte

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

Arno Schmidhauser

November 2006

Seite 3

JPA Java Persistence API

Enumerations
Eine Aufzhlung ist eine Klasse mit einer abschliessend definierten Menge von benannten Objekten. Wichtige Teile einer Enumeration sind: Der Name (die Klasse) der Aufzhlung als Ganzes Die Namen der einzelnen Aufzhlungselemente Eventuell der zugrundeliegende Wert (int, String usw.) Eine Enumeration wird intern wie eine Klasse behandelt, und besitzt auch usserlich die Eigenschaften einer Klasse: Sie gehrt zu einem Package Sie kann public, protected, private sein sie kann Attribute und Methoden besitzen

Enumerations ersetzen die bisher verwendeten Konstanten in Form statischer Klassen- oder Interface-Variablen. Enumerations sind in Java-API Dokumentation auf der gleichen Ebene wie Klassen, Interfaces, Exceptions und die noch zu besprechenen Annotations aufgefhrt. Im Rahmen der JPA Spez. werden Enumeration innerhalb von Annotations verwendet, zur Beschreibung verschiedenster Optionen, beispielsweise CascadeType.PERSIST, CascadeType.REMOVE, LockModeType.READ, LockModeType.WRITE usw. In diesen Beispielen sind CascadeType und LockModeType die Aufzhlungen, PERSIST, REMOVE, READ, WRITE die Elemente der Aufzhlungen. Auch als Datentyp fr persistente Daten kommen Enumerations in Frage, zum Beispiel fr die Status-Beschreibung von Objekten. In der Datenbank knnen wahlweise der Name des jeweiligen Elementes einer Enumeration (in obigen Beispielen "PERSIST", "REMOVE" etc.) oder die dahinterliegenden Ordnungszahlen (0, 1 usw.) gespeichert werden. Die nachfolgende Anotation beim jeweiligen Klassenmember einer Entitiy wird dazu verwendet. @Enumerated(STRING) meineEnumerationMember oder @Enumerated(ORDINAL) meineEnumerationMember

Arno Schmidhauser

November 2006

Seite 4

JPA Java Persistence API

Enumerations, Beispiel 1
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. public enum Status { OPEN, CLOSED } ... Status s1 = Status.OPEN; Status s2 = Status.CLOSED; System.out.println( s1.toString() ); System.out.println( s1.ordinal() ); if(s1 == s2) System.out.println("Beide gleich."); Status s3 = Status.valueOf( "OPEN" ); Status all[] = Status.values(); for( int i = 0; i < all.length; i++ ) { System.out.println( all[i].toString() ); }

Obiges Beispiel zeigt die Definition und den Gebrauch einer einfachen Enumeration: Zeile 1 definiert die Enumeration Status. Die Definition ist in einem File Status.java abzulegen. Zeile 3 und 4 erstellen je eine Variable (Objekt) dieser Enumeration. Zeile 5 liefert den String "OPEN" als Ausgabe. Zeile 6 liefert die Zahl 0 als Ausgabe. Fr das Element CLOSED wre die Ausgabe die Zahl 1. Zeile 7: Zwei Elemente sind gleich, wenn sie denselben Namen haben. Das folgend Stck Code liefert bei der Bedingung true als Ergebenis: Status s1 = Status.OPEN; Status s2 = Status.OPEN; if ( s1 == s2 ) // liefert true Zeile 8: Die Methode valueOf() ermglicht das dynamische Erzeugen eines EnumElements auf Grund eines gleichnamigen Strings. Zeile 9: Die Methode values() liefert smtliche Elemente der Enumeration.

Arno Schmidhauser

November 2006

Seite 5

JPA Java Persistence API

Enumerations, Beispiel 2
Eigene Attribute und Methoden 1. public enum Status { 2. OPEN( "offen" ), CLOSED( "geschlossen" ); 3. protected String zusatzWert; 4. Status( String zusatzWert) { 5. this. zusatzWert = zusatzWert; 6. } 7. public String getZusatzWert(){ 8. return this. zusatzWert; 9. } 10. }

Das Verhalten ist grundstzlich gleich wie im vorhergehenden Beispiel. Ein Enumeration-Element kann im Code beispielsweise wie folgt deklariert werden: Status s = Status.OPEN Zustzlich kann von s die Methode getZusatzWert() aufgerufen werden, wie bei jedem anderen Java-Objekt eine definierte Methode aufgerufen wird, z.B. System.out.println( s2.getZusatzWert() ); Der Konstruktor muss private oder unspezifiziert sein.

Arno Schmidhauser

November 2006

Seite 6

JPA Java Persistence API

Annotations
Annotations fgen dem Java-Code zustzliche Informationen hinzu, die nicht ausgefhrt, aber zur Laufzeit abgefragt werden knnen. Annotations sind typsicher, sie werden vom Compiler geprft. Annotations gehren zu Paketen, Klassen, Konstruktoren, Membern, Parametern, zu Annotationen und lokalen Variablen. Annotations werden intern als Interfaces behandelt.

Arno Schmidhauser

November 2006

Seite 7

JPA Java Persistence API

Annotation, Beispiel fr Definition


1. 2. 3. 4. 5. 6. 7. 8. 9. 10. import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @interface MyAnnotation { String was() default "Testen"; String[] wann(); Status status() default Status.OPEN; }

In diesem Beispiel wird eine Annotation definiert. Die Annotation ist einem File MyAnnotation.java abzulegen und zu kompilieren, wie Klassen, Interfaces, Exceptions und Enumerations. Zu 2: Das Target bestimmt, worauf die Annotation angewendet werden kann. In diesem Fall auf eine Klasse oder ein Interface (TYPE). Ist kein Target angegeben, ist jedes Target erlaubt. Zu 3: Die Retention (bersetzt etwa "Vorbehalt" oder "Halterung") bestimmt, ob die Annotation nur im Source-Code (SOURCE), zustzlich im kompilierten ByteCode(CLASS), oder auch in der laufenden Applikation(RUNTIME). Default ist CLASS. Im JPA haben alle Annotations die Retention RUNTIME. Zu 4: Wenn @Inherited angegeben ist, gilt die Annotation auch fr abgeleitete Klassen. Zu 5: Wenn @Documented angegeben ist, soll die Annotation in Dokumentation-Tools (javadoc) dokumentiert werden. Zu 6: Die eigentliche Annotation. Zu 7-9: Als Felder der Annotation knnen Strings, primitive Typen, Enumerations, andere Annotationen, oder Arrays von all diesen angegeben werden. Andere Klassen, ausser die Klasse Class, sind nicht erlaubt. Felder ohne Default mssen beim Gebrauch der Annotation angegeben werden, ansonsten tritt ein Compilerfehler auf. Im Rahmen der JPA Spez. sind Annotations mit komplexer Struktur durchaus blich (z.B. @JoinTable). Eine Klasse kann auch ohne Weiteres mehrere vorangestellte Annotationen haben. Die Annotationen @Target, @Retention, @Inherited und @Documented heissen MetaAnnotationen, weil es Annotationen fr Annotationen sind.

Arno Schmidhauser

November 2006

Seite 8

JPA Java Persistence API

Annotation, Beispiel fr Anwendung


1. 2. 3. 4. 5. @MyAnnotation( was="Pruefen", wann={ "Anfang", "Ende" } ) public class MyAnnotatedClass { // ... }

Mit diesem Stck Code ist der Klasse MyAnnotatedClass eine Annotation des Typs MyAnnotation zugewiesen. Die gesamte Persistenz-Information im JPA wird via Annotations definiert. Der Vorteil liegt darin, dass die Angaben bereits zur Kompilationszeit typengeprft werden knnen. Der Nachteil liegt darin, dass unter Umstnden sehr viel Meta-Information im Source Code steht, insbesondere ber das Mapping von Klassen-Feldern auf Datenbank-Felder. Damit kann der Source Code sehr unbersichtlich werden, respektive muss neu aufgebaut werden.

Arno Schmidhauser

November 2006

Seite 9

JPA Java Persistence API

Annotations abfragen
1. public class AnnotationsAbfragen { 2. public static void main( String args[] ) 3. throws Exception 4. { 5. Class c = Class.forName("MyAnnotatedClass"); 6. for (Annotation a : c.getAnnotations()) { 7. System.out.println( a.toString() ); 8. } 9. } 10. }

10

Mit der toString()-Methode erhlt man eine textuelle Darstellung zurck: @pmq.MyAnnotation(status=OPEN, was=Pruefen, wann=[Anfang, Ende]) Die toString()-Methode ist fr die Analyse von Annotationen meist zu ungenau. Man kann daher mit der Methode Annotation.annotationType() ein Objekt der Klasse Class abholen, und dieses dann mittels des Reflection API weiter analysieren.

Arno Schmidhauser

November 2006

Seite 10

JPA Java Persistence API

Generics
Bis Java 1.4: Object als generische Klasse. Beispiel: List list = new LinkedList(); list.add( new String( "foo" ) ); list.add( new Integer( 7 ) ); String s = (String) list.get(0) Vorteil: bersichtlich und einfach. Nachteile: nicht typsicher, eine Collection kann flschlicherweise Objekte verschiedener Klassen enthalten. schwach dokumentiert, Klasse der Collection unklar. Hufige Cast-Operation erforderlich.
11

Arno Schmidhauser

November 2006

Seite 11

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 ntig 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 mglich, 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, knnen keine spezifischen Methoden, wie etwa das Zusammenzhlen von Kantengewichten implementiert werden. Dieses Problem lsen die Generics mit Wildcards (siehe weiter hinten). Generische Klassen knnen 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 ); } Fr generische Interfaces gilt dasselbe wie fr 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 Mglichkeit einer strengen Typenprfung zur Kompilationszeit. Werden generische Klassen, die mit unterschiedlichen Typparametern instanziert sind, als inkompatibel angesehen, ist diese strenge Typprfung gewhrleistet, und es knnen 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

JPA Java Persistence API

Generics und Vererbung


Generics fhren keine neue Art der Verbung ein! 1. Nicht erlaubt: List<Person> list1 = new LinkedList<Manager>(); 2. Erlaubt: List<Person> list1 = new LinkedList<Person>(); list1.add( new Person() ); list1.add( new Manager() );

15

Der erste Fall betrifft die Kompatibilitt der generischen Typen selbst. Hier gilt das im vorhergehenden Beispiel gesagte. Die Verwandtschaft von Typargumenten begrndet keine Verwandtschaft der generischen Typen als Ganzes. Damit wird eine hohe Typsicherheit zum Kompilationszeitpunkt erreicht. Der zweite Fall betrifft die Kompatibilitt in einer normalen Vererbungshierarchie. Hier will man natrlich nicht vom bisherigen Verhalten von Java abweichen. Ein Objekt der untypisierten Klasse Person kann jederzeit durch ein Objekt der abgeleiteten und untypisierten Klasse Manager vertreten werden.

Arno Schmidhauser

November 2006

Seite 15

JPA Java Persistence API

Generics, Wildcards
1. Wildcardvariable zur Aufnahme irgendeines Typs einer generischen Klasse: Node<?> x = new Node<Integer>(); 2. Wildcard, eingeschrnkt auf Zahlen: Node<? extends Number> x; x = new Node<Integer>(); // ok x = new Node<String>(); // Fehler 3. Einschrnkung des Typparameters einer generischen Klasse: public class NumberNode< T extends Number > { ... }

16

Im Fall 1 kann die Variable x nun einen beliebigen generischen Typ aufnehmen. Die Situation ist gegenber der Deklaration Objekt o = new Node<Integer>(); anders, indem x nur Node-Typen aufnehmen kann. Im Fall 2 kann man die mglichen Zuweisungen an die Variable x weiter einschrnken, indem x nur noch Nodes mit Zahlentypen entgegennehmen kann. Die Variable x kann gemss Fall 1 und Fall 2 ein Objekt der Klasse NodeNumber entgegennehmen. Im Fall 3 wird der Typparameter einer generischen Klasse eingeschrnkt (sogenannter Typebound). Beispielsweise kann man sagen, dass ein Node-Typ nur Sinn macht, wenn er eine Zahl enthlt. In diesem Fall kann man in der Klassendefinition bereits darauf abstellen, dass das Typargument sicher alle Methoden der Klassen Number kennt, und man kann diese auch im Code verwenden. In diesem Beispiel stellt die Methode getDouble() darauf ab, dass als Typargument eine Zahl angegeben wird. public class NumberNode<T extends Number> { protected T aNumber; public double getDouble() { return aNumber.doubleValue(); // content ist mindestens Number } } Die Modifikation von Objekten ber eine Wildcard-Variable oder einen Typebound ist nicht erlaubt. Der folgende Code fhrt zu einem Kompilationsfehler, weil x keine Kenntnis ber den erlaubten Parameteryp von setContent() hat. Node<?> x = new Node<Integer>( 7 ); x.setContent( 5 ); // Kompilations-Fehler // ebenso zum Beispiel fr Listen List<?> list = new LinkedList<Integer>; list.add( 123 ); // Kompilations-Fehler

Arno Schmidhauser

November 2006

Seite 16

JPA Java Persistence API

Frage: Wie ist folgende Deklaration aus der Java Standard Edition zu lesen? Collections.sort( List<T> list, Comparator<? super T> c ) Antwort: Die Operation sort() kann eine Liste vom Typ T sortieren. Der Comparator c muss Objekte vom Typ T vergleichen knnen. Damit dies mglich ist, mssen die Parameter der Methode compare( K obj1, K obj2 ) den Typ T oder eine Unterklasse davon haben. K muss also gleich T oder eine Oberklasse von T sein. Beispiel: import java.util.*; public class MySorter { public static void main( String args[] ) { List<Double> myList = new Vector<Double>(); myList.add( new Double( 3.14 ) ); myList.add( new Double( 1.41 ) ); Collections.sort( myList, new DoubleComparator() ); for ( Double d : myList ) { System.out.println( d.toString() ); } } } class DoubleComparator implements Comparator<Double> { public int compare ( Double n1, Double n2 ) { return Double.compare( n1, n2 ); } } Statt einen Comparator fr Double-Werte, knnte man einen allgemeineren Comparator fr Number erstellen. Dieser Comparator kann auch sehr grosse Zahlen miteinander vergleichen: class NumberComparator implements Comparator<Number> { public int compare ( Number n1, Number n2 ) { return ( (new BigDecimal(n1.toString())).compareTo( new BigDecimal(n2.toString())) ); } } Da ein Double ein Spezialfall einer Number ist, kann ohne weiteres der NumberComparator auf Double angewendet werden. Number ist ein Obertyp von Double, womit die Deklaration Comparator<? super T> nun verstndlich ist.

Arno Schmidhauser

November 2006

Seite 17

JPA Java Persistence API

Generics, Eigenschaften
Generics sind Typkontrollen, die zur Kompilationszeit vorgenommen werden. Nach der Kompilation wird die Typinformation mehrheitlich gelscht im Byte-Code: // Vor Kompilation class Node<Number> { private T content; } // Nach Kompilation class Node { private Object content; }

Keine Abfrage der Typinformation zur Laufzeit mit Reflection. Statische Attribute knnen keinen generischen Typ haben. Primitive Typen (int, double, byte, char) knnen nicht als Typargumente verwendet werden. Keine Arrays von Typvariablen erlaubt.
18

Nach der Kompilation wird nur noch soviel Typinformation ber die Klasse und ihre Methoden in den Bytecode eingebaut, wie der Compiler fr die bersetzung anderer Java-Klassen bentigt. Aus Sicht der JVM ist die Typinformation fr das Laufzeitsystem entfernt. Das Reflection API kann das Typargument nicht eruieren. Jedoch kann der Typ von Objekten an sich ermittelt werden. Um den Typ einer Collection festzustellen, msste man den Typ der Objekte darin ermitteln, was grundstzlich ber Reflection mglich ist. Weil sich alle Typen einer generischen Klasse eine einzige Implementation dieser Klasse teilen, sind die statischen Attribute nur einmal vorhanden und tragen zur Laufzeit den Typ Object. Wrden zur Laufzeit den Klassenvariablen verschiedene Typen von Objekten zugewiesen, zum Beispiel Double, Integer und String, entstnden vllig unvorhersgesehene Resultate, respektiv dynamische ClassCastExceptions beim Lesen und Schreiben dieser Objekte. Statische Attribute knnen daher keinen Typ haben. Primitive Typen knnen zur Laufzeit nicht als Objekte vom Typ Object behandelt werden, was aber mit der Art der Realisierung von Generics in Java notwendig wre. Primitive Typen sind daher nicht erlaubt. Arrays von Typvariablen sind nicht mglich, weil der Typ zu Laufzeit nicht mehr bekannt ist, und damit die Speicherallokation nicht definiert ist.

Arno Schmidhauser

November 2006

Seite 18

JPA Java Persistence API

II bersicht Java Persistence API

19

Referenz: JSR 220, EJB 3.0 Java Persistance API, Final Release 2. Mai 2006 Kurzform in diesem Skript: JPA oder JAPI Spez.

Arno Schmidhauser

November 2006

Seite 19

JPA Java Persistence API

Moderne Persistenz API's


Arbeiten mit gewhnlichen Java-Klassen fr Daten (POJO's) Objekte knnen transient oder persistent sein Innerhalb und ausserhalb von Appservern verwendbar Vererbung, Aggregation, Komposition abbildbar Transitive Persistenz oder Persistence by Reachabiltiy Lazy Fetching Automatic Dirty Checking Datenbank-Roundtrips minimieren, Outer Join Fetching SQL-Generierung zur Laufzeit

20

Mit der Lsung des JPA in EJB 3.0 wurden wesentliche Verbesserungen ( ) erreicht werden gegenber EJB 2.x Technologien. Das JPA ist ein Mix der Erfahrungen aus dem EJB 2.x Ansatz und dem JDO Konzept. JDO ist ein sehr ambitiser Ansatz, mit dem Anspruch, den Source Code 100% von Mapping und Verhaltensangaben frei zu halten.

Arno Schmidhauser

November 2006

Seite 20

JPA Java Persistence API

Technologie Stack
Session Beans / Java Applikation Persistence API Persistence API Implemetation JDBC API JDBC - Driver SQL RDB
21

Java 1. & Annotations EJB 3.0 Spezifikation Hibernate & CGLIB, ... JDBC 3.0 Herstellerspez. SQL 2003 oder Dialekte

Auf der logischen Ebene sind Java-Annotations das Kernstck des JPA. Mit Ihnen wird das Mapping und das Verhalten der persistenten Klassen gesteuert. Die Anforderungen des Dirty Checking, Lazy Fetching und der transitiven Persistenz erfordern viel Aufwand in der JAPI Implementation: Wenn immer ein Programm eine Modifikation an Datenobjekten vornimmt, muss vorerst sichergestellt werden, dass das Objekt sich berhaupt im Speicher befindet (Lazy Fetch, Eager Fetch). Dann muss aufgezeichnet werden, dass das Objekt modifiziert wurde (Dirty Checking) und letztlich muss aufgezeichnet werden, ob ein Objekt zum CommitZeitpunkt transient oder persistent ist. Im letzteren Fall muss sein Zustand in die Datenbank zurckgeschrieben werden. Als Realisierungstechniken kommen typischerweise Source Code Modifikationen beim Deployment oder Byte Code Enhancement zur Kompilations- oder Laufzeit in Frage. Hibernate verwendet Byte Code Enhancement zur Laufzeit, indem jede Datenklasse ber einen eigenen Classloader geladen wird. Dieser Classloader modifiziert den Code so, dass fr jede Zugriffsoperation (Zuweisung eines Wertes, Increment/Decrement, Dereferenzierung, Array-Index-Auflsung) zustzlich Verwaltungs-Code eingeschoben wird. Wichtige Datentypen, z.B. Set und List werden durch eigene Hibernate-Typen ersetzt. Dieses Verhalten ist gut im Debugger ersichtlich!

Arno Schmidhauser

November 2006

Seite 21

JPA Java Persistence API

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

Arno Schmidhauser

November 2006

Seite 22

JPA Java Persistence API

Entities
Kennzeichnung mit Annotation @Entity Klasse kann Basisklasse oder abgeleitet sein Klasse kann abstrakt oder konkret sein. Serialisierbarkeit ist bezglich Persistenz nicht erforderlich. Anforderungen: 1. No-Arg Konstruktur muss vorhanden sein. 2. Klasse darf nicht final, kein Interface und keine Enumeration sein und keine final-Methoden enthalten. 3. Felder mssen private oder protected sein. 4. Zugriff von Clients auf Felder nur ber get/set- oder Business-Methoden erlaubt. 5. Jede Entity muss einen Primrschlssel (@Id) haben.

23

Die Anforderungen 1 bis 4 sind technisch bedingt. Sie erlauben vernnftige Realisierungen der Persistenz, indem das Byte-Code Enhancement beispielsweise abgeleitete Klassen erzeugt, mit denen eine Applikation dann effektiv arbeitet. Die Anforderung 3 garantiert, dass nur ber die abgeleiteten- Methoden auf Felder zugegriffen wird, und in diesen Methoden das Persistenz-Management stattfinden kann.

Arno Schmidhauser

November 2006

Seite 23

JPA Java Persistence API

Entity, Beispiel
@Entity public class Message { @Id protected long id; protected String sender; protected String receiver; protected int priority; @Temporal( TemporalType.TIMESTAMP ) protected Date date; public Message() {} // get, set and business methods }
24

Sehr pragmatisch gelst ist das Mapping der Felder auf Datenbanktabellen: Ohne zustzliche Angaben werden der Klassenname als Tabellenname und die Feldnamen als Spaltennamen verwendet. Mit folgenden Annotations kann man andere Mappings vornehmen: @Entity @Table( name="MyMessageTable" ) public class Message { // ... @Column( name="senderName" ) protected String sender; // ... } Zahlreiche weitere Mappingangabe, z.B. Datenbank- und Schema-Name bei der Tabelle, sind mglich. Siehe Kapitel 9.1 JPA Spez. Fr gewisse Datentypen kann man Przisierungen anbringen, z.B. @Temporal fr die Java Typen Date und Calendar. Mit @Temporal wird angegeben, ob das Mapping in ein Datenbankattribut als DATE, TIME oder TIMESTAMP verstanden werden soll.

Arno Schmidhauser

November 2006

Seite 24

JPA Java Persistence API

persistente Datentypen
erlaubt: Alle primitiven Typen, String Alle Wrapperklassen und serialisierbaren Typen (z.B. Integer, BigDecimal, Date, Calendar) byte[], Byte[], char[], Character[] Enumerations Beliebige weitere Entity-Klassen Collections von Entities, welche als Collection<>, List<>, Set<> oder Map<> deklariert sind. Nicht erlaubt: Alle Arten von Arrays ausser die obgenannten Collections von etwas anderem als Entities, also z.B. Wrapperklassen und andere serialiserbare Typen.
25

Der Umfang an persistenten Typen ist fr die meisten Anwendungen ausreichend. Fr technische Anwendungen knnte das Verbot von Array-Typen lstig sein. Dazu ist allerdings zu sagen, dass z.B. Hibernate wesentlich weiter geht als die JPA Spez., indem Arrays und Collections fr smtliche Arten von Objekten erlaubt sind, insbesondere auch Arrays von primitiven Typen. Felder von serialisierbaren Klassen, sei es von Java vordefinierte oder eigene Klassen, sind ebenfalls persistent. Whrend einige vordefinierte Typen wie Date, Calendar, Integer, BigDecimal usw. in die entsprechenden SQL-Typen gemapped werden, findet bei eigenen serialisierbaren Klassen einfach nur eine Ablage des serialisierten Byte-Stromes in der Datenbank statt (varbinary Datentyp o..). Solche Objekte knnen nicht in einer Abfragebedingung von Queries auftreten. Zur Problematik der Elementreihenfolge in Listen siehe Kapitel IV. Transiente Felder knnen mit Java transient oder mit der @Transient Annotation markiert werden.

Arno Schmidhauser

November 2006

Seite 25

JPA Java Persistence API

Java-Type / SQL-Type Mapping


Implizit durch JDBC 3.0, Data Type Conversion Table, definiert. Explizit durch die @Column Annotation, z.B. ... @Column( name="sender", columnDefinition="VARCHAR(255) NOT NULL" ) protected String sender; ... Produktspezifisch durch Framework (Hibernate) oder im JDBC-Driver fr die jeweilige Datenbank.

26

JDBC 3.0 Mapping

Arno Schmidhauser

November 2006

Seite 26

JPA Java Persistence API

Access-Typ
Es gibt zwei Zugriffspunkte fr das Persistenz-Framework auf die Daten einer Klasse: das eigentliche Datenfeld, oder die zugehrigen set()/get() Methoden. Field-based Access @Entity public class Message { @Id protected long id; } Property-based Access @Entity public class Message { protected long id; @Id public long getId() {...} public void setId( long id ) {...} }
27

Welche Zugriffsart vorliegt, ist durch die Position der Mapping-Annotations gegeben. Alle Mapping Annotations mssen entweder bei Feldern stehen ( Field-based Access) oder bei der get() Methode fr eine Eigenschaft. Bei Property-basedAccess mssen alle set- und get-Methoden zwingend paarweise vorkommen. Bei Field-based Access knnen get-Methoden oder anders benannte Methoden fr den Zugriff auf private Felder verwendet werden. Bei Property-based Access wird in der Datenbank der von der get()-Methode zurckgelieferte Wert abgelegt, beim Erzeugen des Objektes in der Applikation wird die set()-Methode fr das Setzen des Zustandes verwendet. Wre also beispielsweise ein Personename in einem Java-Objekt in Kleinschreibung abgelegt, die getMethode liefert jedoch immer eine Grosschreibung zurck, so enthlt die Datenbanktabelle den Personennamen in Grosschrift.

Arno Schmidhauser

November 2006

Seite 27

JPA Java Persistence API

Entity Identity - Primrschlssel


Jede Entity-Klasse muss einen mit @Id bezeichneten Primrschlssel besitzen. Primrschlssel knnen 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 gewhlte 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 Primrschlssel
Es wird eine spezielle ID-Klasse bentigt. 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 Primrschlsseln mglich wird, z.B. beim Aufruf der find() oder getReferenceMethode(), um bestimmte Objekte in der Datenbank zu finden. Genau genommen wird ja auch bei einfachen Primrschlsseln 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 mssen public oder protected sein. 5. Die hashCode() und equals()-Methode mssen implementiert werden. 6. Name und Typ der einzelnen Felder/Properties mssen 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 Primrschlsselobjekt 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 zugehrige Datentabelle sieht in beiden Fllen(@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

JPA Java Persistence API

Persistence Unit
Eine Persistence Unit ist eine logische Einheit von Entities. Sie wird beschrieben durch: Einen Namen Die zu dieser Unit gehrenden 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.xml abgelegt.
31

Der Name ist eine Identifikation, um in der Applikation dafr eine EntityManagerFactory erstellen zu knnen. Die Entity-Klassen werden als Information fr den Class Transformer und die Generierung der SQL-Befehle angegeben. Der PersistenceProvider ist die implementationsspezifische "Urklasse" fr das Persistenzmanagement in dieser Persistence Unit. Sie liefert die EntityManagerFactory zurck. Der Transaktionstyp (transaction-type) wird als JTA oder RESOURCE_LOCAL angegeben. eine JTA Persistence Unit muss u.A. verteilte Transaktionen ber eine XA-Schnittstelle erlauben. Die Datenquelle kann als JNDI-Name angegeben werden im Rahmen einer J2EE-Umgebung. Je nachdem ob die Datenquelle JTA-fhig ist (verteiltes Transaktionsmanagement) verwendet man den Tag <jta-data-source> oder <non-jata-data-source>. Falls nicht mit JNDI gearbeitet wird, knnen in den Properties die blichen JDBC-Verbindungsparameter angegeben werden. Beispiel: <persistence> <persistence-unit name="PMQ" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <class>pmq.PMQ</class> <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.connection.driver_class" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/> <property name="hibernate.connection.username" value="hibuser"/> <property name="hibernate.connection.password" value="hibuser"/> <property name="hibernate.connection.url" value="jdbc:sqlserver://localhost;databaseName=hib_db"/> <property name="dialect" value="org.hibernate.dialect.SQLServerDialect"/> </properties> </persistence-unit> </persistence>

Arno Schmidhauser

November 2006

Seite 31

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 fr diese Entities Die laufende Transaktion Der Contexttyp @PersistenceContext(type=PersistenceContextType.EXTENDED) public class MessageProducer { // ... EntityManagerFactory emf = Persistence.createEntityManagerFactory("PMQ"); EntityManager em = emf.createEntityManager(); Transaction tx = em.getTransaction(); // ...
32

Arno Schmidhauser

November 2006

Seite 32

JPA Java Persistence API

Persistence Context, Contexttyp


Contexttyp TRANSACTION Lesender und schreibender Zugriff nur innerhalb der Transaktion. Gelesene Objekte sind nach der Transaktion im Zustand detached. Wiedereinkopplung in eine Transaktion mit merge(). Contexttyp EXTENDED Alle Objekte sind lesend und schreibend zugreifbar. Modifikationen finden lokal statt. Effekt von persist(), remove() usw. wird aufbewahrt. Propagation von Efffekten und nderungen in die DB aber nur, wenn nachtrglich begin()/commit() ausgefhrt wird.

33

Bei Hibernate ist fr Java SE Applikationen immer der Transaktionstyp EXTENDED gesetzt. Ein Rollback fhrt in beiden Contexttypen dazu, dass bereits geladene Entities in den Zustand detached versetzt werden. Nach einem Rollback sollte nicht mehr mit den bestehenden Enitities gearbeitet werden (Kap. 3.3.2 JPA Spez.), weil der lokale Zustand beibehalten, der Zustand in der DB jedoch zurckgesetzt wird. Das kann z.B. Probleme mit generierten Primrschlsseln verursachen, weil eine bereits vergebene ID seitens Datenbank nochmals vergeben wird. Dasselbe gilt sinngemss fr Versionennummern bei optimistischem Concurrency Control. Datenbankzugriffe arbeiten gemss Spezifikation im Isolationsgrad read committed. Dies gilt auch fr den Contexttyp Extended.

Arno Schmidhauser

November 2006

Seite 33

JPA Java Persistence API

Erzeugen persistenter Objekte


// ... EntityManagerFactory emf = Persistence.createEntityManagerFactory("PMQ"); EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Message m = new Message( ... ); em.persist( m ); tx.commit(); // ...

34

Arno Schmidhauser

November 2006

Seite 34

JPA Java Persistence API

Kaskadierte Persistenz (1)


Kaskadierte Persistenz heisst: Alle von einem persistenten Objekt aus erreichbaren Objekte sind ebenfalls persistent. PMQ pmq = new PMQ( "Dringendes" ); em.persist( pmq ); Message m = new Message( ... ) pmq.append( m ); m wird automatisch persistent // ... em.remove( pmq ); m wird ebenfalls aus der db gelscht

Es spielt keine Rolle, ob die persist() Methode vor oder nach der append()-Methode aufgerufen wird!

35

Arno Schmidhauser

November 2006

Seite 35

JPA Java Persistence API

Kaskadierte Persistenz (2)


In JPA muss die Kaskadierung deklariert werden.

Die Kaskadierung kann fr das Erstellen und das Lschen der Persistenz separat eingestellt werden. public class PMQ { @OneToMany( cascade={CascadeType.PERSIST, CascadeType.REMOVE } ) List<Message> messages; // ... } Achtung: Die Kaskadierung bezieht sich nur auf die persist() und die remove()-Methode, nicht etwa auf das Ein- oder Austragen in der Message-Liste mit gewhnlichen Java-Methoden!

36

Die Kaskadierung kann auch auf die Methoden EntityManager.refresh() und EntityManager.merge() bezogen werden, mit CascadeType.REFRESH resp. CascadeType.MERGE.

Arno Schmidhauser

November 2006

Seite 36

JPA Java Persistence API

Aufsuchen persistenter Objekte


// ... tx.begin(); Message m = em.find( Message.class, Long.parseLong(1) ); // oder ... Message m = em.getReference(Message.class, Long.parseLong(1)); // oder ... Query qry = em.createQuery( "select m from Message m where m.id = 1 ); List list = qry.getResultList(); Iterator it = list.iterator(); // ... tx.commit(); // ...
37

Es sind zwei Varianten fr das Auffinden von Objekten vorgestellt: Suchen via Primrschlssel mit find() oder getReference() Es muss die Klasse und der Primrschlsselwert im gleichen Datentyp wie in der Entity definiert bergeben werden. Die Methode find() liefert null, wenn das Objekt nicht existiert, die Methode getReference() erzeugt eine Exception, wenn das Objekt nicht existiert (sptestens beim ersten eigentlichen Zugriff auf das Objekt). Die Methode find() ldt auf jeden Fall die unmittelbaren Felder des gesuchten Objektes. Suchen mit einer Abfrage Objekte werden via eine Abfrage gesucht. Das Resultat wird als Liste zurckgegeben. Die resultierende Liste hat keinen Typparameter. Dies deshalb, weil der Resultattyp vom Query abhngt und dieses in jedem Fall dynamisch ausgewertet wird. Die Typsicherheit wrde sich durch Verwendung von Typparametern nicht erhhen. Im Weiteren ergben sich Schwierigkeiten, wenn die select-Klausel mehrere Objekte enthlt, das heisst eine Liste von Arrays zurckgeliefert wird. Ein Array von Typvariablen ist in Java nicht erlaubt.

Arno Schmidhauser

November 2006

Seite 37

JPA Java Persistence API

Aufgaben
bung Persistent Message Queue PMQ. Erstellen einer Grundausstattung.
MessageProducer main() verwendet MessageConsumer main() verwendet

Entity PMQ id qname append() get() getAll() remove() isEmpty() size()

messages 0..*

Entity Message sender receiver priority date id

38

Aufgaben 1. Hibernate-Umfeld und Datenbank aufsetzen. 2. Entity-Klassen PMQ und Message definieren. 3. persistence.xml zusammenstellen. 4. Datenbanktabellen erstellen. 5. Producer (MessageProducer.java) und Consumer (MessageConsumer.java) fr Messages programmieren, ev. Zusatzprogramm zum erstellen einer leeren PMQ (PMQInit.java). 6. Testen.

Arno Schmidhauser

November 2006

Seite 38

JPA Java Persistence API

III Beziehungen zwischen Entities

39

Arno Schmidhauser

November 2006

Seite 39

JPA Java Persistence API

Beziehungen
Beziehungen zwischen Entities sind prinzipiell gegeben durch entsprechende Referenzen oder Collection-Member in den Entity-Klassen. Sie mssen jedoch deklariert werden, und sehr oft sind Details zum O/R-Mapping und zum Verhalten notwendig. Folgende Beziehungs-Charakteristiken spielen eine Rolle: Unidirektional, bidirektional One to One, One to Many, Many to One, Many to Many Vererbung Aggregation, Komposition

Der korrekte Unterhalt der Beziehungen ist Sache des Programmes!


40

Der Unterhalt der Beziehungen obliegt dem Programm, respektive dem Entwickler. Wird beispielsweise bei einer bidirektionalen Beziehung eine Message in einer Message Queue eingetragen, muss das Programm selber in der Message die Referenz auf die MessageQueue nachfhren. Das Persistenz-Framework tut das nicht. Die Deklaration der Beziehungen ist eine Mapping-Hilfe fr das Framework und bringt keine zustzliche Programmautomation mit sich!

Arno Schmidhauser

November 2006

Seite 40

JPA Java Persistence API

One To One, Unidirektional


Entity PMQ id 1..1 owner id Entity Owner

@Entity public class PMQ { @OneToOne( optional=false ) @JoinColumn( name="owner_id" ) protected Owner owner; // ...

41

Die Beziehung geht rein von der linken Seite aus. Es ist an sich nicht spezifiziert, ob ein Owner mehrere PMQ's oder nur eine PMQ haben kann. Die Owner-Klasse beinhaltet keine Angaben zu dieser Beziehung. Die Abbildung wird mit einem Fremdschlssel in der PMQ-Tabelle realisert. Mit @OneToOne( optional=false) wird przisiert, dass immer ein Owner vorhanden sein muss. Die Angabe bezieht sich also auf das Zielobjekt. In der Tabelle sollte sich dies darin wiederspiegeln, dass der Fremdschlssel nicht null sein darf. create table "PMQ" ( id qname owner_id ); Die obige Tabelle darf durchaus eine foreign key Klausel enthalten. Sie wurde hier nur aus bersichtgrnden weggelassen. bigint nvarchar(64) bigint not null identity primary key, not null unique, not null

Arno Schmidhauser

November 2006

Seite 41

JPA Java Persistence API

One To One, Bidirektional


Entity PMQ id 0..1 1..1 owner id Entity Owner

@Entity public class PMQ { @OneToOne( optional=false ) @JoinColumn( name="owner_id" ) protected Owner owner; // ... @Entity public class Owner { @OneToOne( mappedBy="owner", optional=true ) protected PMQ pmq; //...
42

Die Klasse PMQ bleibt gleich wie in der unidirektionalen Variante. Auch die Datenbanktabelle fr PMQ bleibt sich gleich, und die Owner-Tabelle muss nicht angepasst werden. Die Angabe mappedBy="owner" bezieht sich auf das Feld owner in der Klasse PMQ, und damit ist indirekt gesagt, dass der Fremdschlssel sich auf der PMQ-Tabelle befindet. Mit optional=true kann das Owner-Objekt bestehen bleiben, wenn die PMQ gelscht wird. Programmatisch wird nicht kontrolliert, ob eventuell dasselbe Owner-Objekt fr zwei PMQ-Objekte verwendet wird. Dies ist nur zu kontrollieren, indem ein uniqueConstraint auf den owner_id Fremdschlssel gesetzt wird. Damit knnen nicht zwei PMQ-Eintrge in der Datenbank mit demselben owner_id existieren. Bei bidirektionalen Beziehungen spricht man oft vom Besitzer. Der Besitzer ist in diesem Beispiel die PMQ-Klasse. Der Besitzer ist die Klasse, welche nicht die mappedBy-Angabe enthlt.

Arno Schmidhauser

November 2006

Seite 42

JPA Java Persistence API

One To Many, Unidirektional


Entity PMQ id 0..* messages id Entity Message

@Entity public class PMQ { @OneToMany @JoinTable( name="PMQ_Message", joinColumns={ @JoinColumn( name="PMQ_id" ) }, inverseJoinColumns={ @JoinColumn(name="messages_id") } ) protected List<Message> messages = new Vector<Message>(); // ...
43

Eine unidirektionale OneToMany-Beziehung wird mit einer Assoziationstabelle abgebildet, nicht mit einer direkten Primrschlssel-/ Fremdschlssel-beziehung. Begrndung an diesem Beispiel: Eine Message kann nicht nur zu einer PMQ, sondern vielleicht noch zu anderen Klassen gehren. Wenn die Zugehrigkeit in eine (oder mehrere) Assoziationstabelle ausgelagert ist, braucht es nicht fr jede Zugehrigkeit in der Message-Tabelle ein Fremdschlssel-Attribut, von denen dann viele nicht belegt sind. Obige Angaben in der @JoinTable-Annotation entsprechen dem Default-Verhalten: Der Name der Assoziationstabelle setzt sich aus den Namen der beiden EntityKlassen zusammen. Die Attributnamen der Assoziations-Tabelle ergeben sich wie im folgenden Beispiel fr die Tabellendefinition gezeigt, aus einer Kombination von Tabellenname links + id-Feld links, Beziehungsname links + id-Feld rechts. create table "Message" ( id ); create table "PMQ" ( id -- ... ); create table "PMQ_Message" ( PMQ_id messages_id ); bigint bigint not null, not null unique bigint not null primary key, bigint not null identity primary key, -- ...

Arno Schmidhauser

November 2006

Seite 43

JPA Java Persistence API

One To Many, Bidirektional


Entity PMQ id messages 0..1 0..* Entity Message id pmq

@Entity public class PMQ { @OneToMany( mappedBy = "pmq" ) protected List<Message> messages = new Vector<Message>(); //... @Entity public class Message { @ManyToOne( optional=true ) @JoinColumn( name="PMQ_id" ) protected PMQ pmq; //...
44

Die bidirektionale Beziehung wird als enger wie die unidirektionale angesehen und deshalb per Default als direkte Primrschlssel-/Fremdschlssel Verknpfung realisiert. Die Annotation @JoinTable entfllt. Hingegen wird in der Klasse PMQ angegeben, welches Feld (nicht Tabellenattribut!) in der Message-Klasse fr die Beziehung verantwortlich ist. Diese Angabe wird durch mappedBy="pmq" in der OneToMany-Annotation vorgenommen. In der Message-Klasse ist die Rckbeziehung durch die @ManyToOne-Annotation deklariert. Die @JoinColumnAnnotation deklariert den Namen des Fremdschlssels. Achtung: Mit nur dem Java-Code wre nicht zweifelsfrei festgehalten, dass es sich um eine echt bidirektionale Beziehung handelt. Das Feld Message.pmq knnte ja auf eine andere PMQ zeigen als diejenige von der es referenziert wird, und eine andere Bedeutung haben. Zu beachten ist auch der Unterschied zwischen einer ManyToOne 1..1 oder 0..1 Beziehung: Es wird entweder @ManyToOne( optional=false ) oder @ManyToOne( optional=true ) angegeben. Ausserdem ist die Tabellendefinition fr den Fremdschlssel mit null respektive not null zu deklarieren. create table "Message" ( id PMQ_id -- ... ); Fr die @OneToMany-Annotation gibt es keine optional=false respektive optional=true Angabe. Auf der Many-Seite gibt es also nur die Multiplizitt 0..n. Andere Multiplizitten, insbesondere 1..n, mssen programmatisch und allenfalls ber Triggers in der Datenbank realisiert werden (check-Constraints sind nicht ausreichend, da der delete-Befehl keine Prfung von check-Constraints auslst). bigint bigint not null identity primary key, null -- foreign key to PMQ table

Arno Schmidhauser

November 2006

Seite 44

JPA Java Persistence API

Many To One, Unidirektional


Entity Owner id 1..1 0..* Entity Address id owner

@Entity public class Address { @ManyToOne( optional=false ) @JoinColumn( name="Address_id", referencedColumnName = "id" ) protected Owner owner; // ...

45

Die unidirektionale ManyToOne-Beziehung drfte eher selten sein. Der Default-Name fr das Fremdschlssel-Attribut ist der Name der Ursprungsklasse + "_" + Name des Primrschlsselfeldes in der Zielklasse. Das obige Beispiel gibt diese Default-Benennung wieder.

Arno Schmidhauser

November 2006

Seite 45

JPA Java Persistence API

Many To Many, Bidirektional


Entity PMQ Entity Message

@Entity id id messages pmqs public class PMQ { 0..* 0..* @ManyToMany @JoinTable( name="PMQ_Message", joinColumns={@JoinColumn(name="pmqs_id" ) }, inverseJoinColumns={@JoinColumn(name="messages_id") } ) protected List<Message> messages = new Vector<Message>(); // ... @Entity public class Message { @ManyToMany( mappedBy = "messages" ) protected List<PMQ> pmqs = new Vector<PMQ>(); // ...
Abbildung mit Assoziationstabelle. create table "PMQ_Message" ( pmqs_id messages_id ); bigint bigint not null, not null

46

Die unidirektionale ManyToMany-Beziehung entspricht aus Sicht Java Code der unidirektionalen OneToMany-Beziehung. Auf obiges Beispiel bezogen kann eine Message also zu mehreren Queues gehren, die Message kennt diese jedoch nicht. Nur die PMQ's kennen ihre Messages. Die Message-Klasse enthlt damit das Feld pmqs gar nicht. Seitens Datenbank ist die Situation ebenfalls identisch zur OneToMany Beziehung, ausser dass der zweite Fremdschlssel nicht unique ist: create table "PMQ_Message" ( PMQ_id messages_id ); Fr die @ManyToMany-Annotation gibt es keine optional=false respektive optional=true Angabe. Es existiert also nur die Multiplizitt 0..n. Andere Multiplizitten, insbesondere 1..n, mssen programmatisch und allenfalls ber Triggers in der Datenbank realisiert werden (check-Constraints sind nicht ausreichend, da der delete-Befehl keine Prfung von check-Constraints auslst). bigint bigint not null, not null unique

Arno Schmidhauser

November 2006

Seite 46

JPA Java Persistence API

Komposition / Aggregation
Entity PMQ id messages id 0..* Entity Message

@OneToMany( cascade={CascadeType.PERSIST, CascadeType.REMOVE}) protected List<Message> messages;


Entity PMQ id messages id 0..* Entity Message

@OneToMany( cascade={ CascadeType.PERSIST} ) protected List<Message> messages;


47

Die Komposition ist in der Regel ergnzt sein um eine 1..1 Beziehung von rechts nach links, im obigen Fall zum Beispiel: @ManyToOne( optional=false ) protected PMQ pmq; Achtung: Ohne Angabe der kaskadierten Lschung kann es auch mit einer deklarierten 1..1 Beziehung von rechts nach links zu Inkonsistenzen auf der Datenbank kommen. Der Befehl em.remove( pmq ) wrde nmlich klaglos ablaufen, weil innerhalb des Programmes die beteiligten Objekte noch korrekt verknpft sind, in der Datenbank jedoch nur die PMQ, nicht die Message gelscht wird. Datenbankseitig knnte man dies durch einen not null constraint auf dem Fremdschlssel in der Message-Tabelle verhindern. Die Aggregation kann ergnzt sein um eine 0..1 Beziehung von rechts nach links, im obigen Fall zum Beispiel: @ManyToOne( optional=true ) protected PMQ pmq;

Arno Schmidhauser

November 2006

Seite 47

JPA Java Persistence API

Vererbung, Grundstzliches
Vererbungshierarchien knnen problemlos verwendet und abgebildet werden. Klassen knnen abstrakt oder konkret sein. Alle Klassen in der Vererbungshierachie mssen den Primrschlssel der Basisklasse verwenden (erben). Es gibt vier Mappingstrategien auf die Datenbank: Eine einzige Tabelle fr die gesamte Verbungshierarchie Eine Tabelle fr jede konkrete Klasse Eine Tabelle fr 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

SINGLE_TABLE Strategie Die Attribute der Basisklasse gelten fr jeden Datensatz. Attribute von abgeleiteten Klassen sind nur belegt, wenn der Datensatz ein Objekt dieser Klasse reprsentiert. Es ist ein Typ-Attribut erforderlich, dass fr jeden Datensatz angibt, zu welcher Klasse das reprsentierte Objekt gehrt. Vorteile: Kompakt, Abfragen ber alle Objektarten sind einfach zu handhaben. Nachteile: Viele Attribute ohne Belegung mit Werten. Attributzuordnung zu Klasse nicht ersichtlich. Fehlverhalten mglich, indem Attributen Werte zugeordnet werden, die gar keine haben drften Integrittsbedingungen erforderlich. Anwendung: Hauptinformation in der Basisklasse, abgeleitete Klassen nicht zu umfangreich, oder insgesamt wenig Attribute zu implementieren. TABLE_PER_CLASS Strategie Es wird nur pro konkrete Klasse eine Tabelle erstellt. Die Attribute einer abstrakten Basisklasse werden in jeder Tabelle der abgeleiteteten Klasse erstellt. Fr die Basisklasse selbst wird keine Tabelle erstellt. Vorteile: Jede Tabelle entspricht genau einer Klasse. Es sind keine nicht-belegten Attribute vorhanden. Nachteil: Abfragen ber mehrere Klassen hinweg erfordern union-Abfragen (kann teilweise Probleme mit SQL geben). nderungen an der Basisklasse erfordern nderungen in allen Tabellen der abgeleiteten Klassen. Anwendung: Basisklasse nur schwach belegt und abstrakt. Meistens werden nur konkrete und einzelne, abgeleitete Klassen in einer Applikation bentigt. JOINED Strategie Jede Tabelle enthlt nur die Attribute ihrer Klasse. Der Primrschlssel der Basistabelle wird in allen abgeleiteten Klassen ebenfalls als Primrschlssel verwendet und gleichzeitig als Fremdschlssel auf die Basistabelle. Vorteile: Konzeptionelles Modell im Tabellenmodell noch ersichtlich. Punktuelle nderungen am Konzept haben nur punktuelle nderungen in den Tabellen zur Folge. Mit dieser Variante ist es datenbankseitig mglich, dass ein Objekt in mehreren Klassen vorkommt! Beispielsweise kann in einem CRM-System eine Person gleichzeitig Kunde und Lieferant sein. Nachteile: Gewisse Arten von Abfragen sind umstndlich, beispielsweise wenn man wissen will, zu welcher Klasse ein Datensatz in der Basistabelle gehrt. Es muss dann in jeder abgeleiteten Tabellen nach einem entsprechenden Datensatz gesucht werden. Anwendung: Viele Attribute, sowohl in der Basisklasse, wie in den abgeleiteten Klassen vorhanden. Mehrstufige Vererbungshierarchie. Flexible Datenstruktur fr unterschiedlichste Bedrfnisse und Abfrage-Arten erforderlich. Hufige nderung des Datenmodells wahrscheinlich. Mapped Superclass Strategie Die Basisklasse ist selbst keine Entity, stellt aber Attribute zur Verfgung fr die abgeleiteten Klassen, welche als Entities deklariert sind. Die abgeleiteten EntityKlassen bernehmen die Felder von der Basisklasse, welche nicht transient deklariert sind. Die Felder werden in den Tabellen der abgeleiteten Entity-Klasse abgelegt. Kann angewendet werden, 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

JPA Java Persistence API

Vererbung, 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, SINGLE_TABLE-Mapping
@Entity @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn( name="messageType" ) public abstract class Message { @Id @GeneratedValue protected long id; // ... @Entity @DiscriminatorValue("SM") public class SimpleMessage extends Message {} @Entity @DiscriminatorValue("IM") public class ImageMessage extends Message {}
51

Falls kein Discriminatorattribut angegeben wird, ist "DTYPE" der Default. Falls kein Discriminatorwert angegeben wird, ist der Klassenname der Default. Alle Tabellenattribute, welche nicht zur Basisklasse gehren, drfen null-Werte enthalten. create table "Message" ( id messageType sender receiver priority date content image contentType description xmlContent ); bigint varchar( 64 ) nvarchar( 64 ) nvarchar( 64 ) int datetime nvarchar( max ) varbinary( max ) nvarchar( 64 ) nvarchar( 255 ) xml not null default primary key, not null, not null, not null, not null, not null, null, null, null, null, null

Arno Schmidhauser

November 2006

Seite 51

JPA Java Persistence API

Vererbung, JOINED-Mapping
@Entity @Inheritance(strategy=InheritanceType.JOINED) public abstract class Message { @Id @GeneratedValue protected long id; // ... @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 );

not not not not not

null identity primary key, null, null, null, null,

create table "SimpleMessage" ( id bigint not null primary key, content nvarchar( max ) not null, foreign key ( id ) references Message( id ) ); create table "ImageMessage" ( id bigint not null primary key, image varbinary( max ) not null, contentType nvarchar( 64 ) not null, foreign key ( id ) references Message( id ) ); create table "XMLMessage" ( id bigint not null primary key, xmlContent xml not null foreign key ( id ) references Message( id ) );

Das TABLE_PER_CLASS Mapping funktioniert sinngemss.

Arno Schmidhauser

November 2006

Seite 52

JPA Java Persistence API

Aufgaben
Implementieren Sie die verschiedenen Beziehungstypen am PMQ-Beispiel: OneToOne, unidirektional, Aggregation OneToMany, unidirektional, Komposition ManyToMany, bidirektional, Aggregation Vererbung

53

Arno Schmidhauser

November 2006

Seite 53

JPA Java Persistence API

IV Spezielles

54

Arno Schmidhauser

November 2006

Seite 54

JPA Java Persistence API

Listen
Listen haben eine implizite Ordnung. Datenbanktabellen haben keine Ordnung. Die Abbildung von Listen in Tabellen ist im JPA nicht vollstndig transparent, sondern muss durch Annotations beschrieben werden. JPA Lsung @OneToMany @OrderBy( "date ASC" ) List<Message> messages; Hibernate Lsung @OneToMany @IndexColumn(name="position" ) List<Message> messages;

55

Fr die @IndexColumn-Annotation ist noch die Hibernate-spezifische Annotation zu importieren: import org.hibernate.annotations.IndexColumn; Wird kein @OrderBy angegeben, findet die Sortierung zum Ladezeitpunkt nach dem Primrschlssel statt. Die Sortierung wird nicht automatisch aufrecht erhalten zur Laufzeit. Bei Hibernate ist @OrderBy nur erlaubt, wenn das Mapping nicht mit einer Assoziationstabelle, sondern direkt mit Fremdschlssel/Primrschlssel erstellt wird. Es wird daher das Arbeiten mit der Hibernate-Annotation @IndexColumn empfohlen. Dies hat den Vorteil, dass die Positionierung nicht in der Entity-Tabelle (Message), sondern der Assoziationstabelle abgelegt ist. Damit kann die Positionierung auch im Fall einer ManyToMany-Beziehung abgebildet werden, wo eine Message in verschiedenen PMQ's verschiedene Positionen haben kann. @OrderBy erfordert ausserdem ein geeignetes Attribut fr die Sortierung (das vom Programm unterhalten wird). Mit IndexColumn kann ein spezielles, generisches Attribut definiert werden, das vom Framework gepflegt wird.

Arno Schmidhauser

November 2006

Seite 55

JPA Java Persistence API

Die Abbildung von Listen tendiert zu Performance-Problemen. Wird beispielsweise keine @IndexColumn definiert in Hibernate, werden alle Beziehungsdatenstze beim Zurckschreiben in die Datenbank gelscht und neu eingefgt. Ist die @IndexColumn definiert, wird beim Anfgen eines neuen Elementes auch nur der neue Datensatz in der Tabelle eingefgt. Das Einfgen eines Elementes zwischendurch hat jedoch einen Update auf allen Eintrgen der Assoziationstabelle zur Folge: Fr jede Position wird der Fremdschlssel des Elementes an dieser Position neu gesetzt. Hibernate generiert fr eine OneToMany Beziehung folgende Beziehungstabelle fr die Liste: create table PMQ_Message ( PMQ_id messages_id position bigint bigint bigint not null, not null, not null,

primary key ( PMQ_id, position ), unique ( messages_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

JPA Java Persistence API

Neben Listen werden auch Maps untersttzt. Der Nutzen von Maps fr Entities ist allerdings fraglich. Maps ersetzen lediglich das Suchen von Objekten via Primr- oder Fremdschlssel. Beispiel: import org.hibernate.annotations.IndexColumn; // @Entity public class PMQ { @JoinTable( name="PMQ_Message", joinColumns = { @JoinColumn( name="pmqs_id" ) }, inverseJoinColumns = { @JoinColumn( name="messages_id" ) } ) @MapKey( name="position" ) protected Map<Long, Message> messages; // Beispiel fr das Arbeiten mit Arrays in Hibernate: import org.hibernate.annotations.IndexColumn; // @OneToMany @JoinTable( name="EventDate", joinColumns = @JoinColumn(name="id", referencedColumnName="id") ) @Column( name="eventDate" ) @IndexColumn( name="position" ) protected Calendar eventDates[] = new Calendar[10]; //

create table "EventDate" ( id eventDate position ); bigint datetime smallint not null, not null, not null

Arno Schmidhauser

November 2006

Seite 57

JPA Java Persistence API

Persistente Enumerations
Enumerations knnen persistent sein. In der Datenbank wird entweder der Ordinalwert (Position) oder der Stringwert (Name der Konstante) abgelegt. @Entity public class MutableMessage extends SimpleMessage { @Enumerated(EnumType.ORDINAL) protected MessageStatus status; oder // ... @Enumerated(EnumType.STRING)

58

Default fr die Datenbankablage ist ORDINAL. Der Datentyp in der Datenbank kann varchar oder int sein. varchar ist sowohl fr den Stringwert wie fr die Zahl geeignet. Anmerkung: Hibernate hat Schwierigkeiten mit der Datenbank, wenn bei bestehenden Daten der Ablagetyp gendert wird Tabellenwerte gemss aktueller Einstellung der @Enumerated-Annotation anpassen.

Arno Schmidhauser

November 2006

Seite 58

JPA Java Persistence API

Embedded Classes
Komposition: Mutterobjekt mit eingebetteten Objekten. Eingebettete Objekte haben keine eigene Identitt. Mutterobjekt und eingebettete sind in derselben Tabelle abgelegt. @Entity public class PMQ { @Id @GeneratedValue private long id; @Embedded protected Owner owner; // ... } @Embeddable public class Owner { protected String name; //... }

59

Klassen, respektive deren Objekte, knnen in andere Klassen eingebettet sein: Ihre Felder werden in der Datenbank in derselben Tabelle abgelegt wie das Mutterobjekt. Die eingebetteten Objekte haben keine Identitt und sind nicht shareable. Die Einbettung von Collections ist nicht untersttzt. Die Einbettung hat (im Gegensatz zu JDO) nichts mit Serialisierung zu tun. Die Felder der eingebetten Klassen kommen explizit in den SQL-Tabellen vor. Embedded Klassen mssen als solche markiert sein. Selbstdefinierte Klassen, welche Serializable und nicht als Entities gekennzeichnet sind, werden automatisch in die Datenbank serialisiert (Binrer Datentyp in der Datenbank). Solche Felder sind jedoch nicht abfragbar mit JPA Query Language. Embedded Classes sind eine echte Komposition im UML-Sinn. Leider nicht fr OneToMany Beziehungen. create table "PMQ" ( id -- ... name ); varchar( 32 ) not null bigint not null identity primary key,

Arno Schmidhauser

November 2006

Seite 59

JPA Java Persistence API

Sekundr-Tabellen fr Entities
Gelegentlich wird eine Entity in der Datenbank in mehrere Tabelle hineinmodelliert, oder muss umgekehrt aus mehreren Tabellen zusammengesetzt werden. Eine Entity kann beliebig viele Sekundrtabellen haben: @Entity @Table( name = "Message" ) @SecondaryTable( name = "MessageExt", pkJoinColumns = { @PrimaryKeyJoinColumn(name="idRef", referencedColumnName = "id") } ) public class Message { // ...
60

Sollen mehrere Sekundrtabellen verwendet werden, muss die Annotation @SecondaryTables( {@SecondaryTable(...), @SecondaryTable(...), ... } ) verwendet werden. Fr jedes Feld in der Klasse muss die Herkunft spezifiziert werden, beispielsweise: public class Message { @Column( table="Message", name="sender" ) protected String sender; @Column( table="MessageExt", name="receiver" ) protected String receiver; //... create table "Message" id ); create table "MessageExt" idRef bigint -- ... ); Tests mit einer Vererbungshierarchie und einer Sekundrtabelle fr die Basisklasse waren nicht erfolgreich in Hibernate. Ohne Vererbung klappten die Tests jedoch einwandfrei. ( not null primary key, bigint -- ... (

not null identity primary key,

Arno Schmidhauser

November 2006

Seite 60

JPA Java Persistence API

Mapping von SQL-Views


Komplexe Abfragen werden in einer Datenbank gerne als (materialisierte-) View vorgehalten. Um Performance-Vorteile zu nutzen, kann eine View wie eine Entity angesprochen werden. Beispiel: @Entity public class MessageStatistics { @Id protected long id; @Column( insertable=false, updatable=false) protected int numQueues; // ... create view MessageStatistics ( id, numQueues ) as select m.id, count(*) from Message m left outer join PMQ_Message pm on () group by m.id
61

Obiges Beispiel berechnet, in wievielen PMQ's eine Message vorkommt. Die Tabelle PMQ_Message ist die Assoziationstabelle zwischen Message und PMQ. Die Spalten der Klasse sind read only gehalten, indem sowohl das Einfgen, wie der Update verboten ist. Java-technisch msste man das Erzeugen neuer Statistikobjekte noch durch einen entsprechend privaten Konstruktor verbieten.

Arno Schmidhauser

November 2006

Seite 61

JPA Java Persistence API

Callbacks
Callbacks sind eine gngige Methode, um Einfluss auf den Lade- oder Speichervorgang von Objekten zu nehmen. Callbacks werden vom Entwickler definiert, aber vom JPA Framework aufgerufen. Beispiel: @Entity public class ImageMessage extends Message { @PrePersist @PreUpdate protected void compress(){ ... } @PostLoad @PostUpdate protected void uncompress() { ... }

62

Folgende Callback-Annotations sind spezifiziert: @PrePersist: Vor der entsprechenden Datenbank-Operation, nicht zwingend synchron zur persist()-Methode. Unmittelbar vor flush(). @PostPersist: Nach der entsprechenden Datenbank-Operation. Unmittelbar nach flush(). Ein generierter Primrschlssel ist in dieser Methode verfgbar. @PreRemove: Vor der entsprechenden Datenbank-Operation, nicht zwingend synchron zur remove()-Methode. Unmittelbar vor flush(). @PostRemove: Nach der entsprechenden Datenbank-Operation. Unmittelbar nach flush(). @PreUpdate: Vor der entsprechenden Datenbank-Operation. Unmittelbar vor flush(). @PostUpdate: Nach der entsprechenden Datenbank-Operation. Unmittelbar nach flush(). @PostLoad: Nach implizitem Refresh, oder aufruf der refresh()-Methode.

Die Callbacks werden auch fr Objekte, die via kaskadierten Aufruf von persist(), remove() und refresh() betroffen sind, ausgefhrt. Callbacks sind void und haben keinen bergabeparameter. Sie knnen public, protected oder private sein.

Arno Schmidhauser

November 2006

Seite 62

JPA Java Persistence API

Foreign Key Constraints


nderungsoperation von einem Peristenzframework erzeugen oft Konflikte mit Fremdschlsselbedingungen, wenn Datenstze in der falschen Reihenfolge gelscht werden. Hibernate arbeitet mit einer sogenannten "Write Behind Queue" : SQL-Befehle werden so geordnet, dass die Lschung der abhngigen Datenstze zuerst erfolgt.

63

Arno Schmidhauser

November 2006

Seite 63

JPA Java Persistence API

DDL Angaben in Annotations


Die Erzeugung von DDL kann, aber muss nicht durch eine Implementation der JPA Spez. angeboten werden. In der JPA Spez. sind verschiedene Angaben (Annotations und Attribute davon) vorgesehen, welche die Erzeugung von DDL-Befehlen ermglichen. Beispiel: public class Message { @Column (unique = false, nullable=false, columnDefinition="varchar", lenghth=64 ) public BigDecimal sender; // ...

64

In Hibernate kann man via folgende Einstellungen in persistence.xml die Erstellung von DDL-Code beeinflussen. DDL-Code soll von Hibernate jedesmal neu ausgefhrt werden in der Datenbank: <property name="hibernate.hbm2ddl.auto" value="create"/> DDL-Code soll bei nderungen von Hibernate ausgefhrt werden: <property name="hibernate.hbm2ddl.auto" value="update"/>

Arno Schmidhauser

November 2006

Seite 64

JPA Java Persistence API

Aufgaben
Erstellen Sie ein Listenmapping fr die Messages einer PMQ und untersuchen Sie, welche SQL-Befehle erzeugt werden, wenn einer Liste eine neue Message angefgt wird. Zum Tracen von SQL-Befehlen das File /etc/log4j.properties aus dem Hibernate Home in das Applikationsverzeichnis kopieren und anpassen: log4j.logger.org.hibernate.SQL=trace log4j.logger.org.hibernate.type=trace Erstellen Sie eine eingebettete Klasse, zum Beispiel fr den Owner einer PMQ.

65

Arno Schmidhauser

November 2006

Seite 65

JPA Java Persistence API

V Objektverwaltung und Transaktionsmanagement

66

Arno Schmidhauser

November 2006

Seite 66

JPA Java Persistence API

Grundstze
Der Transfer von Objekten von und zur Datenbank erfolgt automatisch: so spt wie mglich Lazy Access. Der Transfer von Objekten von und zur Datenbank kann manuell erzwungen werden synchron zum Aufruf. Selbstverstndlich gilt ein Transaktionsmodell: Der Zugriff auf Objekte erfolgt ab Beginn der Transaktion, die Synchronisation mit der Datenbank wird sptestens beim Commit abgeschlossen und unterliegt der ACID-Regel. Auf Objekte kann auch ausserhalb von Transaktionen zugegriffen werden, jedoch ohne Konsistenz- und Synchronisationsgarantie.

67

Arno Schmidhauser

November 2006

Seite 67

JPA Java Persistence API

Objektzustand
Objekte haben vier mgliche Zustnde: new Objekt ist neu erzeugt, hat noch keinen Zusammenhang mit der Datenbank und noch keine gltige ID. managed Das Objekt hat eine Entsprechung in der Datenbank. nderungen werden vom Entity Manager automatisch getracked und mit der DB abgeglichen. detached Das Objekt hat eine Entsprechung in der Datenbank, wurde aber abgekoppelt. Der Zustand wird nicht mehr automatisch abgeglichen mit der Datenbank. removed Das Objekt existiert noch, ist aber zum Lschen markiert.

68

Ein Objekt ist new wenn es java-mssig instantiiert, aber noch nicht mit persist() bearbeitet wurde. Nach dem persist() ist es managed. Der Aufruf von remove() hat fr ein new-Objekt keine Auswirkungen, fr ein detached-Objekt wird eine IllegalArgumentException geworfen, fr ein managedObjekt wird effektiv der Zustand auf remove gesetzt. Ein Objekt wird detached nach einem Rollback, durch Serialisierung, durch Schliessen des Entity Managers, oder durch Aufruf der clear() Methode des Entity Managers. Eine Methode zum Feststellen des Objektzustandes exisitiert nicht.

Arno Schmidhauser

November 2006

Seite 68

JPA Java Persistence API

Fragen zur Objektverwaltung


1. Ab wann hat ein neues Objekt eine gltige ID (Wenn die ID via Datenbank-Resourcen vergeben wird und nicht manuell)? 2. Zu welchen Zeitpunkten wird der Objektzustand von der Datenbank eingelesen? 3. Zu welchen Zeitpunkten wird ein gendertes Objekt in die Datenbank zurckgeschrieben? 4. In welchem Zustand ist ein Objekt nach dem Commit? Kann es nach der Transaktion weiterverwendet werden? 5. Was passiert mit einem Objekt beim Rollback? 6. Knnen persistente Objekte serialisiert und von einer Applikation in eine anderen transportiert werden?

69

Siehe auch Kapitel 3.2 JPA Spezifikation.

Arno Schmidhauser

November 2006

Seite 69

JPA Java Persistence API

Objekt ID
Ein neues Objekt bekommt erst eine ID, wenn es das erste Mal physisch in die Datenbank transportiert wird. Message m = new Message(...) pmq.append( m ); System.out.println( m.getId() ) em.flush() System.out.println( m.getId() )

Java Default 0 !

gltige ID, z.B. 1

Die persist()-Methode ist eine logische Operation.

70

@Entity public class PMQ implements Serializable { @Id protected long id; protected String qname; @OneToMany( cascade={ CascadeType.PERSIST } ) protected List<Message> messages = new Vector<Message>();

@Entity public class Message { @Id @GeneratedValue long id; // ... public long getId() { return id; } // ... }

Arno Schmidhauser

November 2006

Seite 70

JPA Java Persistence API

Einlesen
Der Objektzustand wird beim ersten Zugriff auf das Objekt eingelesen. Wenn FetchType.EAGER gesetzt ist, werden referenzierte Objekte ebenfalls mitgeladen. Wenn FetchType.LAZY gesetzt ist, werden referenzierte Objekte beim ersten Gebrauch eingelesen. Der Objektzustand wird nie automatisch aufgefrischt, nur via die EntityManager.refresh()-Methode. Eine neue Transaktion fhrt nicht automatisch zum erneuten Einlesen bestehender Objekte.

71

Der FetchType wird bei den Annotationen @Basic, @OneToOne, OneToMany etc. als Attribut angegeben, zum Beispiel: @Entity public class PMQ { @ManyToMany( fetch = FetchType.EAGER ) protected List<Message> messages = new Vector<Message>(); @OneToOne( fetch = FetchType.EAGER ) protected Owner owner; } @Entity public class Message { @ManyToMany( mappedBy = "messages", fetch = FetchType.EAGER ) protected List<PMQ> pmqs = new Vector<PMQ>(); } Defaultwerte fr FetchType: OneToOne EAGER OneToMany LAZY ManyToOne EAGER ManyToMany LAZY

Der Gebrauch von refresh() bezglich Isolationsgrad in der Datenbank muss kritisch betrachtet werden: Bei Isolationsgrad READ_COMMITTED bringt die Methode keine absolute Sicherheit bezglich Aktualitt und Synchronisation mit der Datenbank, bei Isolationsgrad REPEATABLE_READ ist refresh() berflssig. Fr Applikationen, die nie terminieren und die periodisch Daten lesen und anzeigen/weitergeben kann die Funktion jedoch ntzlich sein.

Arno Schmidhauser

November 2006

Seite 71

JPA Java Persistence API

Zurckschreiben
Das Zurckschreiben findet zum Commit-Zeitpunkt oder explizit mit der flush() Methode statt. Das Zurckschreiben beinhaltet kein Refresh allflliger nderungen in der Datenbank in der Zwischenzeit. Das Zurckschreiben betrifft nur nderungen, nicht ungenderte Daten. Applikation m.setContent("A") tx.commit() m.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

72

Bei Hibernate findet der Update so fein wie mglich statt, bei der nderung eines Feldes eines Objektes, taucht nur dieses Feld im SQL-Update-Befehl auf.

Arno Schmidhauser

November 2006

Seite 72

JPA Java Persistence API

Objektzustand nach dem Commit, Weiterverwendung des Objektes


Wenn der Persistence Context EXTENDED ist, bleibt ein Objekt im Zustand managed nach dem Commit. nderungen nach dem Commit werden aufbewahrt und im Rahmen der nchsten Transaktion in die Datenbank bernommen. Wenn der Persistence Context TRANSACTION ist, geht ein Objekt in den Zustand detached ber nach dem Commit. nderungen mssen mit EntityManager.merge() innerhalb der nchsten Transaktion wieder eingekoppelt werden.

73

Der Persistence Context TRANSACTION ist der Normalfall bei Container Managed Entities. Der Persistence Context EXTENDED ist der Normalfall bei Java Standard Applications. Ein Objekt im Zustand detached ist vom EntityManager abgekoppelt. Es kann als eine Art Transfer-Objekt angesehen werden. Es kann serialisiert von einer Applikation in eine andere transportiert werden. Die EntityManager.merge()-Operation erlaubt das Einkoppeln des Objektes in eine Datenbank beim Zielsystem. Das Objekt wird nach folgenden Regeln eingekoppelt: existiert ein Objekt mit derselben ID wird der Zustand des detached Objekt bernommen. exisitiert die ID nicht, wird ein neues Objekt mit einer neuen ID angelegt und der Zustand des detached Objektes bernommen. Weitere Flle siehe Kap. 3.2.4.1

Wurde das Objekt in der Datenbank zwischen dem Detach und dem Merge gendert sind zwei Resultate mglich beim Commit der Transaktion: Das Objekt enthlt eine Versionsnummer (@Version-Annotation) zur Versionenkontrolle. In diesem Fall wird ein Fehler ausgegeben. Das Objekt enthlt keine Versionsnummer, dann wird der Zustand des gemergeden Objektes in die DB zurckgeschrieben. Es entsteht somit ein LostUpdate.

Arno Schmidhauser

November 2006

Seite 73

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 gendert, jedoch der Zustand in der Datenbank. Mgliche Inkonsistenzen. Objekte via Methoden des Entity Manager neu laden: find(), getReference(), createQuery() usw.

74

Arno Schmidhauser

November 2006

Seite 74

JPA Java Persistence API

Serialisierung und Transport


Objekte knnen zwecks Weitergabe in andere Persistenzkontexte oder Applikationen serialisert werden. FileOutputStream fos = new FileOutputStream( "myfile.ser" ); ObjectOutputStream os = new ObjectOutputStream( fos ); os.writeObject( pmq ); os.close();

75

Fr den Serialisierungvorgang wird ein Objekt nicht von der Datenbank geladen. Die Serialisierung funktioniert als nur, wenn die zu serialisierenden Objekte bereits im Cache (Persistence Context) sind. Auf logischer Ebene sichergestellt ist dies gemss EJB Spezifikation (Kap 3.2.4) nur, wenn alle Beziehungen mit fetch=FetchType.EAGER deklariert sind.

Arno Schmidhauser

November 2006

Seite 75

JPA Java Persistence API

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

Arno Schmidhauser

November 2006

Seite 76

JPA Java Persistence API

Fragen zum Transaktionsmanagement


1. Ist der Objektzustand nach dem Lesen in die Applikation in der Datenbank eingefroren, whrenddem die Transaktion luft (Mit welchem Isolationsgrad wird gearbeitet)? 2. Ein Objekt wird gendert: Zu welchem Zeitpunkt wird es in der Datenbank gesperrt? 3. Kann entdeckt werden, ob Daten beim Commit in der Zwischenzeit von anderen Prozessen gendert wurden auf der DB?

77

Siehe auch Kapitel 3.4 JPA Spezifikation.

Arno Schmidhauser

November 2006

Seite 77

JPA Java Persistence API

Isolationsgrad
JPA arbeitet standardmssig im Isolationsgrad READ COMMITTED. Gelesene Daten sind whrend der Transaktion nicht eingefroren. Die Konsistenzprobleme Lost Update und Phantom knnen auftreten.

78

Die Spezifikation geht von einem Isolation Level 1 (READ COMMITTED) aus, respektive einem anderen Isolationsgrad, der keine langen Lesesperren hlt. Das Setzen von strengeren Isolationsgraden ber die EJB-Spez. ist nicht mglich. Hibernate erlaubt jedoch den Durchgriff auf die JDBC-Ebene und damit, abgesehen vom Abholen der JDBC-Connection, einen im Java-Umfeld standardisierten Zugriff auf Transaktionsbefehle. In Hibernate kann der Isolationsgrad entweder entweder via persistence.xml eingestellt werden: <property name="hibernate.connection.isolation" value="8" /> oder via Anzapfen der JDBC-Session: import java.sql.Connection; import org.hibernate.Session; import org.hibernate.ejb.EntityManagerImpl; // Create EntityManagerFactory for a persistence unit called pmq. emf = Persistence.createEntityManagerFactory("PMQ"); // create EntityManager em = emf.createEntityManager(); tx.begin() Connection con = ((EntityManagerImpl)em).getSession().connection(); con.setTransactionIsolation( Connection.TRANSACTION_SERIALIZABLE ); // tx.commit()

Arno Schmidhauser

November 2006

Seite 78

JPA Java Persistence API

Sperrzeitpunkt
Daten werden in der Datenbank im Rahmen von SQLBefehlen gesperrt. Da nderungen vorerst lokal durchgefhrt und erst zum Commitzeitpunkt in die DB propagiert werden, finden das Sperren erst beim Commit statt. Allfllige Wartesituationen, Deadlocks, Integrittsverletzungen treten effektiv erst zum Commitzeitpunkt auf.

79

Arno Schmidhauser

November 2006

Seite 79

JPA Java Persistence API

Explizites Locking
Der Entity Manager stellt einen expliziten lock() Befehl zur Verfgung. Das Verhalten ist stark implementationsabhngig.

Beispiel: em.lock( pmq, LockModeType.WRITE )

80

Explizites Locking ist mit EntityManager.lock( ) mglich, ist jedoch sehr unverbindlich definiert. Die Spezifikation verlangt lediglich, dass mit LockModeType.READ ein Lost Update fr das gelockte Objekt verhindert wird. Die Implementation muss nicht unbedingt synchron mit dem Aufruf von lock() einen Lock setzen, sie kann grundstzlich einfach mit Versionierung arbeiten und die Konfliktprfung damit auf den commit-Zeitpunkt verschieben. Fr LockModeType.WRITE ist von der Spezifikation lediglich verlangt, dass ein Lost Update fr das gelockte Objekt verhindert wird, sowie, wenn mit Versionierung gearbeitet wird, der Versionenzhler erhht wird. Fr nicht versionierte Objekte besteht von der Spezifikation her kein Unterschied zwischen LockModeType.READ und LockModeType.WRITE. Die Arbeitsweise des lock()-Befehles ist sehr schwammig definiert in der JPA Spez. In Hibernate ist der lock()-Befehl sehr ntzlich, weil unmittelbar gelockt wird. Damit ist ein pessimistisches Verfahren synchron zum Applikationsablauf mglich. Daten knnen lesend gesperrt werden, danach in der Applikation ausfhrlich bearbeitet werden, ohne dass die Gefahr zwischenzeitlicher nderungen durch andere Prozesse besteht.

Arno Schmidhauser

November 2006

Seite 80

JPA Java Persistence API

Beispiel, wie Hibernate LockModeType.READ in einen SQL-Befehl umsetzt: select id from PMQ with (updlock, rowlock) where id =? and version =? Der SQL-Befehl verwendet den Primrschlssel und das Versionenattribut (dieses ist deshalb notwendig bei Hibernate, ansonsten arbeitet das Locking nicht). Der gelesene Primrschlssel wird nicht wirklich verwendet, sondern es geht nur darum, mit dem select-Befehl die Sperre auszulsen. Der Befehl wurde von Hibernate spezifisch fr SQL-Server 2005 generiert. Da die Klausel with (updlock, rowlock) produktspezifisch ist, muss sie von Hibernate jeweils fr einen bestimmten SQLDialekt generiert werden. Hibernate kennt den SQL-Dialekt aus dem Konfigurationsfile persistence.xml. Mit obigem SQL-Befehl sind auch Deadlocks ausgeschlossen, weil nicht nur ein Read-Lock sondern einen Update-Lock (updlock) gesetzt wird. Der SQLBefehl zur Realisierung von LockModeType.WRITE luft ebenfalls ber den Primrschlssel und das Versionenattribut: update PMQ set version=? where id=? and version=? Bei diesem Update-Befehl wird gleichzeitig die Versionsnummer um 1 erhht.

Arno Schmidhauser

November 2006

Seite 81

JPA Java Persistence API

JPA Versionierung
Die Versionierung im Rahmen von JPA ist als applikatorisches Locking zu verstehen. Mit der Versionierung kann ein Concurrency Control ber Transaktion hinweg realisiert werden. Mit einer Versionsnummer knnen Lost Updates detektiert und vermieden werden: @Entity public class PMQ { @Version protected long version; // ... create table "PMQ" ( version bigint not null, // ...

82

Die Versionennummer wird beim Lesen von Objekten mitgenommen. Beim Zurckschreiben wird der Wert im Objekt mit dem Wert in der Datenbank verglichen. Sind die beiden nicht gleich, wird die Transaktion zurckgesetzt und eine OptimisticLockException ausgeworfen. Das Versionierungsattribut darf nicht durch die Applikation gendert werden. Als Datentyp fr die Versionierung kommen int, Integer, long, Long, short, Short und java.sql.Timestamp in Frage. Die Zahlentypen sind vorzuziehen, da nur diese garantiert monoton aufsteigend sind. Es knnen gleichzeitig versionierte und nicht-versionierte Entities existieren. Mit der @Version-Annotation wird fr diese Entity das Versioning automatisch eingeschaltet. Der Begriff "Version" ist leicht missverstndlich, weil nicht mehrere Zustandsversionen, sondern nur die Nummer einer einzigen, aktuellen Version verwaltet wird. Dies im Gegensatz zu einigen Datenbanksystemen, welche mit mehreren, vollstndigen Versionen desselben Datensatzes arbeiten knnen. Der Versionierungsmechanismus und die Versionsprfung tritt automatisch in Kraft, wenn ein Versionenfeld definiert ist. Die Versionierung ist ein sehr einfaches applikatorisches Verfahren, das mgliche Konflikte beim Zurckschreiben detektiert. Ein anderes applikatorisches Verfahren, dass Konflikte verhindert, ist das Checkout/Checkin: Liest eine Applikation einen Datensatz, setzt sie gleichzeitig fr eine Marke, dass der Datensatz in Gebrauch ist. Ist diese Marke gesetzt, verpflichten sich andere Applikationen, den Datensatz nur lesend oder gar nicht in Gebrauch zu nehmen. Das Verfahren ist sehr flexibel, die Marke kann um Informationen ber den Bentzer und den Zeitpunkt des Checkout ergnzt werden. Nachteile: Jedes Lesen erfordert auch einen Schreibvorgang (Setzen der Marke). Strzt die besitzende Applikation ab, kommt es nie zu einem Checkin. Nach dem Checkout muss sofort ein Commit durchgefhrt werden, sonst sind Lesevorgnge technisch blockiert.

Arno Schmidhauser

November 2006

Seite 82

JPA Java Persistence API

Aufgaben
Entwickeln Sie die Producer und Consumer-Klasse fr eine Message Queue so, dass Producer und mehrere Consumer gleichzeitig laufen knnen. Es muss ein korrekter Ablauf im Sinne der Queue Semantik garantiert sein: Jede Message wird nur einmal gelesen und angezeigt.

83

Arno Schmidhauser

November 2006

Seite 83

JPA Java Persistence API

VI JPA Query Language

84

Der offizielle Name ist Java Persistence API Query Language. Der Name EJB QL ist fr EJB Version 2 reserviert.

Arno Schmidhauser

November 2006

Seite 84

JPA Java Persistence API

Konzept
1. Auf das EJB-Modell ausgerichtete Abfragesprache zum Suchen und Bearbeiten von Entities, "SQL-Mapper". 2. Abfragen operieren auf dem Klassenmodell aller Entitten, nicht dem SQL-Datenmodell. 3. Gegenber EJB QL 2.1 wesentliche Erweiterungen: direkte Update- und Delete-Operationen OUTER |INNER JOIN, GROUP BY, HAVING Klauseln Projektionen (Abfrage einzelner Attribute resp. Ausdrcke in der Select-Klausel)

85

Zu Punkt 2: Im Objektmodell gibt es keine Fremdschlssel (in der DB schon, aber diese sind in der Query Language nicht verwendbar). Es knnen also nur Felder/Beziehungsattribute verwendet werden, die im Klassenmodell vorhanden sind. Will man beispielsweise alle Messages finden, die zu keiner PMQ gehren, so sind die Abfragen unterschiedlich, je nachdem ob zwischen PMQ und Message eine unidirektionale oder eine bidirektionale Beziehung definiert wurde (das relationale Modell ist fr beide Flle dasselbe). Abfrage bei unidirektionaler Beziehung von PMQ zu Message: select m from Message m where m not in ( select m2 from PMQ q JOIN q.messages m2 ) Abfrage bei bidirektionaler Beziehung zwischen PMQ und Message: select m from Message m where m.pmq is null

Arno Schmidhauser

November 2006

Seite 85

JPA Java Persistence API

Query erzeugen
Ausgangspunkt fr Abfragen ist der Entity Manager mit den Funktionen: Query createQuery( String query ) Query createNamedQuery( String qryname ) Query createNativeQuery( String sql, String mapname )

86

Die erste Funktion ist sicherlich die hufigste Form. Sie bernimmt einen Query Text in der JPA Query Language (JPA QL). In der zweiten Form kann im Java Source Code in Form einer Annotation ein Named Query definiert werden, beispielsweise: @NamedQuery( name="orphans", query=" select m from Message m where m.pmqs is empty " ) Auf dieses Query kann mit createNamedQuery( "orphans" ) Bezug genommen werden. Anwendung: Wenn dasselbe Query hufig gebraucht wird. Die dritte Form ist fr die Ausfhrung von direktem SQL, mit definiertem Mapping auf Felder einer Klasse: createNativeQuery( "select id, sender, receiver from Message", "myMapping" ) @SqlResultSetMapping( name="myMapping", entities={ @EntityResult( entityClass=pmq.Message.class, fields={ @FieldResult( field="id", column="id" ), @FieldResult( field="sender", column="sender" ), @FieldResult( field="receiver", column="receiver" ) } )} )

Arno Schmidhauser

November 2006

Seite 86

JPA Java Persistence API

Query, Beispiele
Unparametrisiertes Query Query q = em.createQuery( "select m from Message m where m.sender = 'Rolf'") Parametrisierte Queries Query q = em.createQuery( "select m from Message m where m.sender = :sender") Query q = em.createQuery( "select m from Message m where m.sender = ?1")

87

Arno Schmidhauser

November 2006

Seite 87

JPA Java Persistence API

Query, Parameterbergabe
bergabe eines Namensparameters Query.setParameter( string name, Object value ) Beispiel q.setParameter( "sender", "Rolf" ); bergabe eines Positionsparameters Query.setParameter( int pos, Object value ) Beispiel q.setParameter( 1, "Rolf" );

88

Fr die bergabe von Datums- und Zeitwerten existieren noch vier weitere Methoden, um festzulegen, welche Teile eines Datums-/Zeitwertes effektiv im Query verwendet werden sollen. setParameter(int pos, Calendar value, TemporalType temporalType) setParameter(int pos, Date value, TemporalType temporalType) setParameter(String name, Calendar value, TemporalType temporalType) setParameter(String name, Date value, TemporalType temporalType) Auch ganze Objekte drfen Parameter sein.

Arno Schmidhauser

November 2006

Seite 88

JPA Java Persistence API

Query, Ausfhrung
Einschrnken 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 Einschrnken der Resultatmenge knnen unter Umstnden performance-kritisch sein. Es ist abzuklren, ob die zugrundeliegende Datenbank entsprechende SQL-Ergnzungen (beispielsweise select top ... ) vertsteht, welche bereits datenbankseitig zum Eliminieren der nicht gewnschten Objekte fhren. Im Regelfall werden die Objekte mit getResultList als java.util.List abgeholt. Eine Liste ist fr sortierte Abfragen der einzig korrekte Datentyp. Fr nicht sortierte Abfragen ist eine Liste nicht strend. Einfachheitshalber wurde deshalb auf eine allgemeine Collection als Rckgabetyp verzichtet. Enthlt die Abfrage atomare Datentypen, beispielsweise in "select m.priority from Message m" werden die atomaren Typen in die entsprechenden Objekttypen umgewandelt. Enthlt die select-Klausel mehrere Werte oder Objekte, wird eine Liste von Arrays zurckgegeben. Mit getSingeResult() wird ein Abfrageresultat abgeholt, dass nur ein einziges Resultat liefert. Erzeugt eine Exception, wenn kein oder mehr als ein Resultat entsteht. Mit executeUpdate() knnen modifizierende Abfragen (update, delete) durchgefhrt werden. Der FlushModeType ist wichtig, wenn vor der Query-Ausfhrung neue persistente Objekte erzeugt, gelscht oder modifiziert wurden. Wenn der FlushModeType auf AUTO gesetzt ist, findet vor der Ausfhrung eine Synchronisation (flush()) zwischen dem Cache und der Datenbank statt, weil das Query gegen die Datenbank abgesetzt wird. Wenn der FlushModeType auf COMMIT gesetzt ist, findet keine Synchronisation statt, und das Query wird gegen den momentan in der Datenbank existierenden Zustand ausgefhrt, ohne Bercksichtigung noch pendenter nderungen der laufenden Transaktion.

Arno Schmidhauser

November 2006

Seite 89

JPA Java Persistence API

Query, Resultate abholen


Query qry = em.createQuery( qrystring ); List result = qry.getResultList(); Iterator itm = result.iterator(); while( itm.hasNext() ) { Object o = itm.next(); if ( o instanceof Object[] ) { for( Object x : (Object[])o ) System.out.println( x.toString() ); } else{ System.out.println( o.toString() ); } }

90

Mit obigem Code-Stck kann jede Art von Query-Resultat abgeholt werden.

Arno Schmidhauser

November 2006

Seite 90

JPA Java Persistence API

Abfragen, 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), ein Pfadausdruck, ein Aggregatausdruck oder ein Wertausdruck oder eine Kombination der drei letzteren. Die Fromklausel enthlt die Grundmengen an Objekten mit einem Referenznamen (der in JPA QL obligatorisch ist) . Beispiel: from PMQ q . Werden mehrere Grundmengen angegeben ohne Join-Bedingung, wird das Kreuzprodukt gebildet wie in SQL. Die Groupklausel, die Havingklausel und die Orderklausel arbeiten sinngemss wie in SQL. Insbesondere sind auch Pfadausdrcke erlaubt und die Orderklausel erlaubt die Zustze ASC und DESC fr die Sortierung. Zu erwhnen ist, dass keine der Klauseln Methodenaufrufe auf den Objekten erlaubt. Andere OO-Abfragensprachen, z.B. JDO Query Language erlauben dies. Die Whereklausel kennt die blichen Vergleiche und Operatoren: =, >, >=, <, <=, <> (not equal), [NOT] BETWEEN, [NOT] LIKE, [NOT] IN, IS [NOT] NULL, AND, OR, NOT, (), [NOT] EXISTS. In der Whereklausel sind die speziellen Prdikate IS [NOT] EMPTY und [NOT] MEMBER OF zu erwhnen. Beide beziehen sich auf Pfadausdrcke, die in Collections enden.

Arno Schmidhauser

November 2006

Seite 91

JPA Java Persistence API

Pfadausdrcke
1. Ein Pfadausdruck ermglicht die direkte Navigation von einem usseren zu inneren, referenzierten Objekten: select q.owner from PMQ q select q.owner.name from PMQ q 2. Ein Pfadausdruck kann in einer Collection enden: select q.messages from PMQ q 3. Ein Pfadausdruck kann nicht ber eine Collection hinweg navigieren: select q.messages.sender from PMQ q

92

Zu 1: der Ausdruck nimmt keine Rcksicht auf die Kapselung der inneren Objekte. Auch wenn das Ojekt owner privat ist, kann darauf navigiert werden. Ist ein Objekt im Pfad null, so fllt die entsprechende Zeile aus dem Resultat weg. zu 2: Ein Pfadausdruck, der in einer Collection endet und in einer select-Klausel steht, ist von der JPA Spez. eigentlich nicht erlaubt. Er soll aber hier als Illustration dienen, insbesondere, weil er beispielsweise in Hibernate zugelassen ist. Ein Pfadausdruck, der in einer Collection endet, ist vorallem fr die Vewendung in der from- oder der where-Klausel vorgesehen. zu 3: Der Versuch, solche Ausdrcke zu verwenden, ist ein hufiger Fehler. Bei genauerer berlegung ist jedoch klar, dass das Konstrukt falsch ist. Wenn es zugelassen wre, ist die Frage, ob sender ein Feld der Collection an sich, oder ein Feld der darin eingebetten Elemente ist. Es wrde also eine Mehrdeutigkeit auftreten. Im Weiteren wre zu bedenken, dass sender wieder eine Collection sein knnte. Damit wiederum kommen Probleme mit im Kreis herum referenzierten Objekten ins Spiel. Aus diesen Grnden wird auf dieses Konstrukt verzichtet in objektorientierten Abfragesprachen (Nicht hingegen im XML-Umfeld: XPath und XQuery bauen genau auf diesen Konstrukten auf).

Arno Schmidhauser

November 2006

Seite 92

JPA Java Persistence API

Join-Abfragen
Ein Join von zwei Grundmengen bedingt eine definierte Beziehung in den beiden Klassen. Beispiele: Alle Messages der PMQ q1 select m from PMQ q join q.messages m where q.qname = 'q1' Alle PMQs mit Messages der Prioritt 1 select distinct q from PMQ q join q.messages m where m.priority = 1 Name jeder PMQ mit Absender aller Messages select q.qname, m.sender from PMQ q left join q.messages m
93

Im ersten Beispiel sind Message-Objekte gesucht. Der rechte Teil der Join-Operation entspricht einer SQL-Join-Bedingung zwischen PMQ und Message. Im zweiten Beispiel sind PMQ-Objekte gesucht. Zu beachten das Schlsselwort distinct, damit jede PMQ nur einmal in das Resultat kommt. Im drittten Beispiel ist die Mglichkeit eines Outer Joins illustriert.

Arno Schmidhauser

November 2006

Seite 93

JPA Java Persistence API

in und exists
Die Operatoren IN und EXISTS sind hnlich wie in SQL, in der Unterabfrage knnen auch Pfadausdrcke verwendet werden: Suche alle Queues mit Meldungen der Prioritt 1 select q from PMQ q where exists ( select m from q.messages m where m.priority = 1 ) Suche alle Messages, die zu einer PMQ von Rolf gehren select m from Message m where m in ( select m from PMQ q JOIN q.messages where q.owner.name = 'Rolf' )
94

Arno Schmidhauser

November 2006

Seite 94

JPA Java Persistence API

is empty, member of
is empty und member of ergeben gut verstndliche Abfragen auf Pfadausdrcken, die in Collections enden:

1. Suche leere PMQs select q from PMQ q WHERE q.messages IS EMPTY 2. Suche nicht-leere PMQs select q from PMQ q WHERE q.messages IS NOT EMPTY 3. Suche PMQ's in denen die Message m vorkommt Message m = ... ; // irgendwoher Query qry = em.createQuery( "select q from PMQ q where ?1 member of q.messages" ); qry.setParameter( 1, m ); List result = qry.getResultList();
95

Grundstzlich sind die beiden Operatoren nicht zwingend notwendig: Beispiel 1 knnte man ersetzen durch select q from PMQ q where not exists ( select m from q.messages ) Beispiel 2 knnte man ersetzen durch select distinct q from PMQ q join q.messages m oder select q from PMQ q where exists ( select m from q.messages ) Beispiel 3 knnte man ersetzen durch select distinct q from PMQ q join q.messages

m where m = ?1

Arno Schmidhauser

November 2006

Seite 95

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.messages m where q.qname = 'q1' ) Alle Messages, die konkret SimpleMessages sind: select m from Message m where m in ( select mm from SimpleMessage mm )
96

Ein instanceof Operator wre ntzlich, existiert aber nicht. Folgende Abfrage fr das Auffinden von Attributen aus SimpleMessage ist mglich: select m.sender, m.receiver, s.content from Message m, SimpleMessage s where s.id = m.id

Arno Schmidhauser

November 2006

Seite 96

JPA Java Persistence API

Erstaunliches ...
Die folgenden Abfragen liefern unter Hibernate (JBoss) ein gltiges Resultat: select distinct q from PMQ q where q.messages.sender = 'Bob' and q.messages.sender = 'Alice'

select distinct q from PMQ q where q.messages.sender = 'Bob' or q.messages.sender = 'Alice'

97

Erstaunlich ist an den beiden Abfragen, dass eine Navigation ber Collections hinweg (q.messages.sender) mglich ist. Dadurch entsteht eine nicht ganz klare Semantik: Bedeutet der Vergleich q.messages.sender = 'bob', dass alle bob heissen mssen, oder nur einer? Im vorliegenden Fall wird der Gleichheitsoperator offenbar als 'mindestens ein' interpretiert. Die Navigation ber Collections hinweg ist vom Standard nicht vorgesehen. Die zweite der obigen Abfragen wird vom Framework in folgenden Join umgewandelt: select distinct pmq.id, from PMQ pmq, PMQ_Message pm1, Message m1, PMQ_Message pm2, Message m2 where pmq.id=pm2.PMQ_id and pm2.messages_id=m3.id and pmq.id=pm1.PMQ_id and pm1.messages_id=m2.id and ( m1.sender='Alice' or m2.sender='Bob' )

Arno Schmidhauser

November 2006

Seite 97

JPA Java Persistence API

Fetch Join
Der Fetch Join erlaubt das Abfragen von Objekten, mit gleichzeitigem Laden assoziierter Objekte. Der Fetch Join ist Teil der JPA Spez. Der Fetch-Join ist ein reines Optimierungs-Hilfsmittel. Der rechte Operand des Fetch Join hat keine Identifikationsvariable und darf in der Abfrage nicht weiter verwendet werden. select q from PMQ q left outer join fetch q.messages where q.qname = 'q1'
98

Der Fetch Join kann sowohl inner wie outer sein. Zu beachten: Die Join-Semantik hat natrlich zur Folge, dass die Abfrage eventuell mehrfach dasselbe Objekt zurckliefert. Ist dies nicht erwnscht, kann mit select distinct gearbeitet werden.

Arno Schmidhauser

November 2006

Seite 98

JPA Java Persistence API

Update und Delete


Gemss Spezifikation knnen auch Update und DeleteOperationen als "Abfragen" durchgefhrt werden. Der Hintergrund drften Performance-berlegungen sein. Beispiel: delete from PMQ q where q.qname = 'q1' die Lschung beinhaltet kaskadierte Lschungen, die im Rahmen von Beziehungen definiert sind. Update- und Delete Befehle fhren nicht automatisch eine Synchronisation zwischen Datenbank und Applikationscontext (Cache) aus.

99

Arno Schmidhauser

November 2006

Seite 99

JPA Java Persistence API

Aufgaben
Erstellen Sie ein Abfrage-Applikation, um die Mglichkeiten von JPA Query Language auszuloten. Testen Sie mit: Abfragen von Objekten mit Bedingungen / Sortierung Abfragen in der Vererbungshierarchie Gibt es Abfragen, die Schwierigkeiten bereiten, aber in SQL grundstzlich mglich wren?

100

Arno Schmidhauser

November 2006

Seite 100

Das könnte Ihnen auch gefallen