You are on page 1of 107

JPA / Hibernate

class SINGLE_TABLE

Parent SINGLE_TABLE

- attributeP: int «col um n»


ID
DISKRIM INAT OR
AT T RIBUT E_P
AT T RIBUT E_A
AT T RIBUT E_B

ChildA ChildB

- attributeA: int - attributeB: int

Alexander Kunkel
Inhaltsschwerpunkte

• Wissen wird anhand aufeinander aufbauender


praktischer Problemstellungen mit Beispielen aufgebaut.
• Primär JPA, nicht Hibernate proprietär. In Büchern zu
Hibernate steht in der Regel die mächtigere aber
proprietäre API von Hibernate im Mittelpunkt.
• Einsatz in JSE nicht JEE.
• Funktionale Aspekte stehen im Vordergrund.
• Schrittweise Einführung unter Vermeidung zu vieler
Details.
• Die Beispiele zeigen auch die resultierenden DB-
Statements.
• Kein XML-Mapping, ausschließlich Annotations
• Das Thema Konfiguration ist nicht Inhalt.

Alexander Kunkel 20.10.08 2


Literatur
• „Java Persistence With Hibernate“ (deutsche Fassung)
– Hanser
– Christian Bauer, Gavin King
• „Java-Persistence-API mit Hibernate“
– Addison Wesley
– Bernd Müller, Harald Wehr
• EJB Spezifikation
– http://java.sun.com/products/ejb/docs.html
• Hibernate Dokumentation
– www.hibernate.org
• „Hibernate und die Java Persistence API“
– Entwickler Press
– Robert Hien, Markus Kehle
• Java Persistence And EJB3 (Vortrag)
– Linda DeMichiel, chief architect EJB3
– http://www.infoq.com/presentations/ejb-3

Alexander Kunkel 20.10.08 3


Inhalt

Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen

Alexander Kunkel 20.10.08 4


Legende
• Beispielsourcecode im Package tst.annotations
tst.annotation

• Hibernate Feature, nicht in JPA


H!

• Unklar, ob dies eine JPA Eigenschaft ist


H?

• Nur JPA 1.0


1.0

• Ab JPA 2.0
2.0

Alexander Kunkel 20.10.08 5


Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien

Alexander Kunkel 20.10.08 6


Arbeitsumgebung HSQLDB
Datenbank HSQL

Starten der
HSQL
Datenbank.

Alexander Kunkel 20.10.08 7


Arbeitsumgebung Beispiele
Vorbereitete Beispielsourcen

Tabellen und
ihre Klassen

Diverse Testprogramme

Alexander Kunkel 20.10.08 8


Arbeitsumgebung Konfiguration für die
Konfiguration DB-Verbindung

Die für die Arbeitsumgebung gewählte Konfigurationsmöglichkeit

Datenbank-Verbindung

Datenbank-dialekt

Statements auf der


Console anzeigen

DB-Schema
automatisch erzeugen

Annotierte Klassen
automatisch finden

Alexander Kunkel 20.10.08 9


Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen

Alexander Kunkel 20.10.08 10


Java Annotations Was sind
Annotations?

• Metadaten direkt im Sourcecode hinterlegen.


• Diese Metadaten können zur Compilezeit oder zur Laufzeit
ausgelesen werden.
• Annotationen können sich auf folgende Elemente beziehen:
– Packages
– Klassen
– Interfaces
– Enumerations
– Methoden
– Variablen
– Methodenparameter
• Es gibt vordefinierte Annotationen
– @Deprecated
– @Override
– @SuppressWarnings

Alexander Kunkel 20.10.08 11


Java Annotationen @Length
Eigene Annotation

• Annotation @Length Annotation @Length SOURCE


CLASS
RUNTIME
– Längenangabe von Stringfeldern als @Retention(RetentionPolicy.RUNTIME)
Annotation. @Target(ElementType.FIELD)
public @interface Length { TYPE
FIELD
int max(); METHOD
} PARAMETER
CONSTRUCTOR
LOCAL_VARIABLE
ANNOTATION_TYPE
PACKAGE

• MyClass MyClass

Testklasse mit annotierten public class MyClass {
String s1;
Stringfeldern.
@Length(max = 32)
String s2;

@Length(max = 15)
String s3;

public String toString() {


return "s1='" + s1 ...
}
}

tst.annotation.length

Alexander Kunkel 20.10.08 12


Java Annotationen LengthChecker
Eigene Annotation

• LengthChecker LengthChecker
– Eine Prüffunktion, die annotierte public class LengthChecker {
Stringfelder eines Objekts auf ihre
public void check(Object obj) {
Länge hin überprüft.
ArrayList<Field> annotatedFields;
try {
annotatedFields = getAnnotatedFields(obj);
for (Field field : annotatedFields) {
Length length = field.getAnnotation(Length.class);
String s = (String) field.get(obj);
if (s != null && s.length() > length.max()) {
System.out.println("Field '"
+ field.getName()
+ "' is too long.");
}
}
}
catch (Exception e) {
}
}
[...]

private ArrayList<Field> getAnnotatedFields(Object obj)


[...]

tst.annotation

Alexander Kunkel 20.10.08 13


Java Annotationen TestLength
Eigene Annotation

• TestLength TestLength
– Das passende Testprogramm dazu. public class TestLength {

public static void main(String[] args) {


MyClass mc = new MyClass();

mc.s1 = "Donald Duck is a JPA evangelist.";


mc.s2 = "Donald Duck is a JPA evangelist.";
mc.s3 = "Donald Duck is a JPA evangelist.";

Field 's3' is too long. LengthChecker checker = new LengthChecker();


checker.check(mc);
Schneidet s3 auf die checker.preserve(mc);
annotierte Länge ab.

System.out.println(mc);
}
}

Konsole
Field 's3' is too long.

s1='Donald Duck is a JPA evangelist.',


s2='Donald Duck is a JPA evangelist.',
s3='Donald Duck is '

tst.annotation.length

Alexander Kunkel 20.10.08 14


Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen

Alexander Kunkel 20.10.08 15


EntityManager
Java Persistence API - Einführung Entity
Beteiligte Komponenten EJB QL, Query-API

• Zugang zu den
Datenbankfunktionalitäten
erhält man mittels dem
EntityManager.
EntityManager
• Es gibt 2 unterschiedliche
Arbeitsweisen:
– Eher objektorientiert mit den
Operationen „persist“,
Objektoperationen JPA-QL, Query-API „update“, „remove“, „find“ und
„merge“.
– Eher relational mit der
Datenbanksprache JPA-QL
und der Query-API.
Entity • Beide Arbeitsweisen beziehen
sich letztendlich auf
Datenobjekte  Entities und
deren Life-Cycle

Alexander Kunkel 20.10.08 16


Vergleich
Java Persistence API - Einführung Objektoperationen
Beteiligte Komponenten EJB QL, Query-API

Objektoperationen JPA-QL, Query-API

• Entityobjekte stehen im • Datenbanksprache JPA-QL


Mittelpunkt der steht im Mittelpunkt der Query-
Datenbankoperationen: API: select, update, delete
persist, remove, merge, find • Entityobjekte in
• Fortsetzung der Operationen Abfrageergebnissen.
über Beziehungen hinweg
(Transitive Persistenz)
Cascade
• Ownership

Alexander Kunkel 20.10.08 17


Java Persistence API - Einführung Entity
Entity

@Entity • Benötigt einen


Markieren der Klasse als public class Person { Defaultkonstruktor.
„Entity“. Ohne weitere @Id
Angaben ist der Name der • Eine Entity-Klasse muss eine
private String name; Top-Level-Klasse sein.
Tabelle der selbe wie der
der Klasse. • Weder die Klasse noch seine
public String getName() { Attribute und Methoden dürfen
return name; ‚final‘ sein.
} • Die für die Persitenz relevanten
Attribut „Name“ als
Primärschlüssel. Attribute müssen public Getter-
public void setName(String name) { und Settermethoden gemäß
this.name = name; der Java-Beans Spezifikation
} haben.
Attribute werden ohne
weitere Angaben • Eine Entity muss einen
automatisch in private String vorname; Primärschlüssel haben.
gleichnamige • Die Annotationen können
Tabellenspalten public String getVorname() { alternativ an den
return vorname; Objektvariablen oder den
gespeichert. Getter-/Settermethoden notiert
}
werden. Field-based access,
public void setVorname(String vorname) {
Property-based access.
this.vorname = vorname; • Achtung! Mischen von Field-
}
based und Property-based
access führt zu Exceptions, die
} nicht auf die Ursache schließen
lassen. Beispiel:
org.hibernate.MappingException:
Could not determine type for: [...]
, for columns: [...]

tst.firstentity

Alexander Kunkel 20.10.08 18


Java Persistence API - Einführung Persistenzkontext
EntityManager - Persistenzkontext EntityManager

EntityManager: Methoden, die in den Beispielen verwendet werden

Alexander Kunkel 20.10.08 19


Java Persistence API - Einführung getTransaction()
EntityManager - Transaktion

• .getTransaction()
– .begin()
– .commit()
– .rollback()
• Leseoperationen können außerhalb einer Transaktion erfolgen.
– .find()
– .getReference()
– .refresh()
• Einige modifizierende Operationen können ebenfalls außerhalb einer Transaktion
gerufen werden. Veränderungen werden in einer Warteschlange zurückgehalten, bis
eine Transaktion eröffnet wird.  !!! Keine Exception
– .persist()
– .merge()
– .remove()
• Einige Operationen können ausschließlich innerhalb einer Transaktion gerufen
werden.  TransactionRequiredException
– .flush()
– .lock()
– Abfragen mit update/delete.

Alexander Kunkel 20.10.08 20


Java Persistence API - Einführung Session
EntityManager - Transaktion

• Leichtgewichtige Session zur Rahmen für eine Transaktion


Datenbank mit 2 möglichen in einer JSE Umgebung:
Lebensdauern
– STANDARD: Für die Dauer einer
einzigen Transaktion gültig EntityManager em = …
EntityTransaction tx = null;
– EXTENDED: Für die Dauer mehrerer try {
Transaktionen gültig tx = em.getTransaction();
• !!! Nicht threadsafe !!! tx.begin();
// do some work
• Mehrere parallele Transaktionen [...]
mittels mehreren parallelen tx.commit();
EntityManagern. }
catch (PersistenceException e) {
• Fehler werden mittels Runtime- if ( tx != null && tx.isActive() )
Exceptions transportiert. tx.rollback();
• Sobald eine Exception während dem throw e; // or display error message
commit auftritt, muss wenigstens ein }
finally {
rollback auf der Transaktion erfolgen, em.close(); // optional
sonst kann man mit dem }
EntityManager nicht mehr sinnvoll
weiterarbeiten.

Alexander Kunkel 20.10.08 21


Java Persistence API - Einführung DB-Insert
Entity

Person Insert
@Entity EntityManager em = HibernateUtil.getEntityManager();
public class Person {
@Id Person person = new Person(); Entity-Objekt erzeugen
private String name; person.setName("Duck");
person.setVorname("Donald");
public String getName() {
return name; em.getTransaction().begin(); Entity-Objekt speichern
} em.persist(person);
em.getTransaction().commit();
public void setName(String name) {
this.name = name;
} Hibernate-Log
insert into Person (vorname, name) values (?, ?)
private String vorname;

public String getVorname() {


return vorname;
DB
}

public void setVorname(String vorname) {


this.vorname = vorname;
}
}

tst.firstentity

Alexander Kunkel 20.10.08 22


Java Persistence API - Einführung DB-Update
Entity

Person Update
@Entity EntityManager em = HibernateUtil.getEntityManager();
public class Person {
@Id Person person = em.find(Person.class, "Duck");
private String name; person.setVorname("Dagobert"); Objekt anhand
Primärschlüssel laden
public String getName() { em.getTransaction().begin();
return name; em.persist(person); Setze neuen Vornamen
} em.getTransaction().commit();
Objekt wieder
public void setName(String name) {
this.name = name; Hibernate-Log speichern
}
select person0_.name … where person0_.name=?
update Person set vorname=? where name=?
private String vorname;

public String getVorname() { DB


return vorname;
}

public void setVorname(String vorname) {


this.vorname = vorname;
}
}

tst.firstentity

Alexander Kunkel 20.10.08 23


Java Persistence API - Einführung DB-Delete
Entity

Person Delete
@Entity EntityManager em = HibernateUtil.getEntityManager();
public class Person {
@Id Person person = em.getReference(Person.class, "Duck");
private String name;
em.getTransaction().begin(); Objektreferenz anhand
public String getName() { em.remove(person); Primärschlüssel laden
return name; em.getTransaction().commit();
} Objekt in DB löschen

public void setName(String name) {


this.name = name; Hibernate-Log
} select person0_.name … where person0_.name=?
delete from Person where name=?
private String vorname;

public String getVorname() { DB Select erfolgt, obwohl


return vorname; das Object lediglich
} mittels ‚getReference‘
adressiert wird.
public void setVorname(String vorname) {
this.vorname = vorname;
}
}

tst.firstentity

Alexander Kunkel 20.10.08 24


Transient
Java Persistence API - Einführung Persistent
Entity - Life-Cycle Detached

Keine Kopie in Mit Kopie in


der Datenbank Operationen der Datenbank
verändern den
Zustand aller
Objekte die sich
im Kontext des Detached
EntityManagers
befinden merge()

load() rollback()
Query.list() commit()
close()
clear()
persist()
new merge()
Transient Persistent
remove()
Removed persist() persist()

Alexander Kunkel 20.10.08 25


Transient
Java Persistence API - Einführung Persistent
Entity - Life-Cycle Detached

Transient (vorübergehend, flüchtig)


Mit ‚new‘ erzeugte Objekte befinden sich zunächst lediglich im Arbeitsspeicher. Dieser Zustand wird
als ‚Transient‘ bezeichnet. Durch Übergabe an ‚persist‘ des Entity-Managers wechselt der Zustand zu
‚Persistent‘.
Zu dem Objekt gibt es keinen korrespondierenden Datensatz in der Datenbank.

Persistent (nicht flüchtig)


Mit ‚new‘ erzeugte Objekte befinden sich zunächst lediglich im Arbeitsspeicher. Dieser Zustand wird
als ‚Transient‘ bezeichnet. Durch Übergabe an ‚persist‘ des Entity-Managers wechselt der Zustand zu
‚Persistent‘.

Detached (gelöst)

Vom Persistenzkontext ‚gelöst‘. Im Unterschied zu ‚Transient‘ war das Objekt persistent und hat noch
den Primärschlüssel, zu dem es immer noch einen Datensatz in der Datenbank gibt.

Removed (gelöscht)

Vom Persistenzkontext ‚gelöst‘. Im Unterschied zu ‚Transient‘ war das Objekt persistent und hat noch
den Primärschlüssel. Ein Datensatz in der Datenbank existiert allerdings nicht mehr.

Alexander Kunkel 20.10.08 26


Java Persistence API - Einführung Detached
Entity - Life-Cycle merge()

• Instanzen „außerhalb“ der Verwaltung eines EntityManagers.


• Bei em.merge immer nur mit den zurückgelieferten Objekten weiterarbeiten.
• Em.merge löst entsprechenden Select gegen die DB aus.
• Standardproblem bei Web-Anwendungen. Lösungsmuster siehe Hibernatedokumentation ...

Alexander Kunkel 20.10.08 27


Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen

Alexander Kunkel 20.10.08 28


@Entity
Java Persistence API @Id
Entity - Primärschlüssel @GeneratedValue
• Eine Entity muss einen Primärschlüssel haben.
• Markieren eines Feldes oder Methode mittels
@Id als Primärschlüssel.
• Nicht zuletzt weil fachliche Schlüssel selten
wirklich eindeutig sind, setzt man in der Regel
auf technische Schlüssel.
• Technische Schlüssel (Surrogatschlüssel) lassen
sich einfach mittels @GeneratedValue
generieren.
• Es gibt mehrere Generatorstrategien.
• Zusammengesetzter Primärschlüssel ist mittels
@IdClass möglich, aber ein wenig umständlich
und lediglich für Legacy-Datenbanken relevant.

@GeneratedValue kann nur im Zusammenhang


mit @Id benützt werden.
Das schließt die Verwendung von
@GeneratedValue für sonstige Ids leider aus.

tst.primarykey

Alexander Kunkel 20.10.08 29


Java Persistence API @GeneratedValue
Entity – Generator für Primärschlüssel

• @GeneratedValue(strategy = ...) legt die Strategie zur Generierung


von Primärschlüsseln fest.
• JPA-Generatorstrategien:
– GenerationType.AUTO – Wählt eine Strategie entsprechend der
zugrunde liegenden Datenbank.
– GenerationType.TABLE – Primärschlüssel werden mittels einer eigenen
Tabelle verwaltet.
– GenerationType.SEQUENCE – Nutzt Sequences wie es sie
beispielsweise in ORACLE gibt.
– GenerationType.IDENTITY – Nutzt spezielle „Identity“ Spalten wie in
MySql, HSQLDB.
• Es gibt noch weitere Hibernate Generatorstrategien

tst.primarykey

Alexander Kunkel 20.10.08 30


Java Persistence API @GenericGenerator
Entity – Eigener Generator

Einfacher Generator
H!

Person

Testcode DB

tst.idgenerator

Alexander Kunkel 20.10.08 31


@Table
Java Persistence API @Column
Entity - Grundlegende Annotationen @Transient

• @Table
– Definiert die primäre Tabelle für die
Entity
– Parameter: name, catalog, schema,
uniqueConstraints
– Fachlich motivierte
Attributkombinationen können
unabhängig vom technischen
Schlüssel auf unique gesetzt werden.
• @Column
– Definiert die Tabellenspalte für das
markierte Feld @Entity
@Table(uniqueConstraints = {
– Parameter: name, unique, nullable, @UniqueConstraint(columnNames = {
updateable, insertable, "name",
"vorname"
columnDefinition, table, length, })
precision, scale })
public class PK_Person {
• @Transient ...

– Markiert ein nicht persistentes Feld


tst.firstentity
tst.primarykey

Alexander Kunkel 20.10.08 32


Java Persistence API @Lob
Entity – Blobs und Clobs

String lebenslauf;
• Benützen von BLOBS und CLOBS
erfolgt unabhängig von der @Lob
public String getLebenslauf() {
Datenbank. return lebenslauf;
• Für Java-Typen: }

– Byte[] (default) public void setLebenslauf(String lebenslauf) {


– byte[] (default) }
this.lebenslauf = lebenslauf;

– Java.sql.Blob (default) Sorgt bei MySql


byte[] foto;
– String dafür, dass
Hibernate nicht
– Char[] @Column(length=65536) den Defaulttyp:
public byte[] getFoto() {
– char[] return foto;
blob (65.536
bytes), sondern
– Java.sql.Clob (default) } mediumblob
• Bei Lobs wird aus dem (16.777.215
public void setFoto(byte[] foto) { bytes) wählt.
Längenconstraint der konkrete this.foto = foto;
Datenbanktyp abgeleitet. MySql }

kennt für einen Blob


unterschiedlich große Typen!

tst.blobs

Alexander Kunkel 20.10.08 33


Java Persistence API @Temporal
Entity - Datumsfelder

• Annotation kann an java.util.Date und


java.util.Calendar angebracht werden
• Es gibt 3 Temporaltypen

• Beispiel:
JDBC-Typ ORACLE-Typ Beispielinhalt ORACLE
Date java.sql.Date date 2007-11-15 00:00:00.0
Temporal.DATE
Date java.sql.Time date 2007-11-15 16:12:50.0
Temporal.TIME
Date java.sql.Timestamp timestamp 2007-11-15 16:12:50.203
Temporal.TIMESTAMP
Calendar java.sql.Date date 2007-11-15 00:00:00.0
Temporal.DATE
Calendar In Hibernate nicht In Hibernate nicht In Hibernate nicht
Temporal.TIME implementiert implementiert implementiert
Calendar java.sql.Timestamp timestamp 2007-11-15 16:12:50.203
Temporal.TIMESTAMP

Alexander Kunkel 20.10.08 34


Java Persistence API Integer Typen
Entity – Integer Typen

Ein numerischer Integertyp wird durch Hibernate ohne Berücksichtigung von


Precision bzw. Length mit fixer Länge auf die Datenbank gemappt. Mapping
von Integertypen bei Oracle:

Java-Typ JDBC-Typ Oracle-Typ

java.lang.Byte Types.TINYINT number(3,0)


java.lang.Short Types.SMALLINT number(5,0)
java.lang.Integer Types.INTEGER number(10,0)
java.lang.Long Types.BIGINT number(19,0)

Alexander Kunkel 20.10.08 35


Java Persistence API @Enumerated
Entity – Enum Typen

Person DB
public class E_Person {
[...]
@Id
private String name;

@Enumerated(EnumType.STRING)
private Familienstand familienstand1;

// default
@Enumerated(EnumType.ORDINAL)
private Familienstand familienstand2;
[...]
}

Testcode Enum
EntityManager em = HibernateUtil.getEntityManager(); public enum Familienstand {
unbekannt, verheiratet, ledig, geschieden;
E_Person person = new E_Person(); }
person.setVorname("Donald");
person.setName("Duck");
person.setFamilienstand1(Familienstand.ledig);
person.setFamilienstand2(Familienstand.ledig);

em.getTransaction().begin();
em.persist(person);
em.getTransaction.commit();

tst.enumtype

Alexander Kunkel 20.10.08 36


Not Null
Entity Length
DB-Constraints Unique
Not Null

ORACLE DDL-Skript:
Längenbeschränkung

Unique

Defaultbelegung von Datenbankfeldern


private String vorname = "Donald";

public String getVorname() {


return vorname;
}

tst.constraints
Foreign Key  Siehe „Beziehungen“
Alexander Kunkel 20.10.08 37
Hibernate Übersicht
Validatoren H!

Alexander Kunkel 20.10.08 38


@Size
Hibernate @Range
Validatoren @Email H!

Beispielvalidierungen

Code zur Überprüfung auf Einhaltung der Validierungen


V_Person person = new V_Person();
person.setVorname("Donald");
person.setName("Duck");
person.setEmail("abc");
person.setGeburtsJahr(1870);
ORACLE DDL-Skript
ClassValidator<V_Person> create table V_Person (
validator = new ClassValidator<V_Person>(V_Person.class); [...]
geburtsJahr number(10,0)
InvalidValue[] check (geburtsJahr>=1900
invalidValues = validator.getInvalidValues(person); and geburtsJahr<=2006),
[...]
for (int i = 0; i < invalidValues.length; i++) { );
System.out.println(invalidValues[i]);
}

Ergebnis aufgrund schwerer Verfehlungen

Da fehlt noch die


Übersetzung ins tst.validation
Deutsche 

Alexander Kunkel 20.10.08 39


Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen

Alexander Kunkel 20.10.08 40


Beziehungen Granularität
Paradigmen Missmatch

• Granularität von Objekten unterscheiden sich class Impedance Missmatch

von Tabellen. Person Adresse

• Im Beispiel werden die Klassen Person und - nam e: String - strasse: String

Adresse auf die Tabelle PERSON abgebildet.


0..*

Telefon

- num m er: String

PERSON TELEFON

«colum n» «colum n»
ID ID
NAM E NUM M ER
ST RASSE PERSON_FK

Alexander Kunkel 20.10.08 41


1:1 Beziehung unidirektional Varianten

class Embedded
• Anhand der 1:1 Beziehung lassen sich schon die
meisten Konstrukte demonstrieren. -
Person

nam e: String -
Adresse

strasse: String

• 4 Möglichkeiten die 1:1 Beziehung zwischen 1 0..1

Person und Adresse zu mappen


– Embedded in eine Tabelle
– Person und Adresse in getrennte Tabellen.
Zusammengehörige Adresse und Person haben
den selben Primärschlüssel.
– Person und Adresse in getrennte Tabellen mit
Foreign Key seitens Person auf Adresse Person
und ggf. einen unique Constraint auf den
Fremdschlüssel.
– Person und Adresse in getrennte Tabellen mit
Foreign Key seitens Adresse auf Person und ggf.
einen unique Constraint auf den Fremdschlüssel.

Alexander Kunkel 20.10.08 42


Cascade (Transitive Persistenz) CascadeType

• Objektoperationen werden mittels des EntityManagers ausgeführt.


• Die Fortsetzung der Operationen über Beziehungen hinweg kann
eingestellt werden.
• Transitive Persistenz  Cascade
• Default: Ohne Angabe wird keine Operation über Beziehungen
hinweg fortgeführt.
• CascadeType
– PERSIST
– MERGE
– REFRESH
– REMOVE
– ALL
• Verwendbar bei:
@OneToOne, @OneToMany, @ManyToOne, @ManyToMany

tst.cascade

Alexander Kunkel 20.10.08 43


1:1 Beziehung unidirektional @Embedded
Embedded @Embeddable

Person
class Embedded
private Em_Adresse adresse;
Person Adresse

- na m e: String - strasse: String @Embedded


1 0 ..1
public Em_Adresse getAdresse() {
return adresse;
}
PERSON

«co lum n»
ID
Adresse
NAM E
ST RASSE
@Embeddable
public class Em_Adresse {
...

Testcode Hibernate-Log HSQLDB


EntityManager em = HibernateUtil.getEntityManager(); insert into Em_Person (id, name, …
call identity()
Em_Person person = new Em_Person();
person.setVorname("Donald");
person.setName("Duck"); DB
Em_Adresse adresse = new Em_Adresse();
adresse.setStrasse("Talstraße");
adresse.setHausnummer("15");

person.setAdresse(adresse);

em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
tst.one2one.embedded

Alexander Kunkel 20.10.08 44


1:1 Beziehung @PrimaryKeyJoinColumn
Shared Primary Key I

class SharedPrimaryKey
Adresse
Person Adresse
@Id
- na m e: String - strasse : Strin g
1 0..1 @GeneratedValue
public Long getId() {
return id; Ein spezieller Hibernate Generator, der sich
} den Wert des Primärschlüssel von der
PERSON ADRESSE
Adresse holt.
« col um n »
ID
« col um n »
ID
Person
NAM E ST RASSE
@Entity
@GenericGenerator(name = "foreignGenerator", strategy = "foreign",
parameters = { @Parameter(name = "property", value = "adresse") })

public class SPK_Person {


private Long id;

@Id
@GeneratedValue(generator="foreignGenerator")
H!
public Long getId() {
return id;
Hier wird zur Belegung des
}
Primärschlüssels der zuvor definierte
Generator benützt.
private SPK_Adresse adresse;

@OneToOne(cascade=CascadeType.ALL)
@PrimaryKeyJoinColumn // default
@PrimaryKeyJoinColumn stellt den
public SPK_Adresse getAdresse() {
Zusammenhalt von Person und Adresse
return adresse;
über identische Primärschlüssel her.
}
[...]
tst.one2one.sharedprimarykey

Alexander Kunkel 20.10.08 45


1:1 Beziehung @PrimaryKeyJoinColumn
Shared Primary Key II

Testcode
EntityManager em = HibernateUtil.getEntityManager();

Em_Person person = new Em_Person();


person.setVorname("Donald");
person.setName("Duck");

Em_Adresse adresse = new Em_Adresse();


adresse.setStrasse("Talstraße");
adresse.setHausnummer("15");

person.setAdresse(adresse);

em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();

em.clear();

SPK_Person
loadedPerson = em.find(SPK_Person.class, person.getId());

System.out.println(person.getAdresse().getStrasse());

call identity() wird


nur einmal gerufen!
Hibernate-Log HSQLDB
insert into SPK_Adresse (id, strasse, …
call identity()
insert into SPK_Person (id, name, …
select spk_person0_.id ... on spk_person0_.adresse_id=spk_adress1_.id where spk_person0_.id=?
tst.one2one.sharedprimarykey

Alexander Kunkel 20.10.08 46


1:1 Beziehung unidirektional Delete Adresse
Delete Adresse – 1. naiver Fehlversuch
Testcode – Delete Adresse
EntityManager em = HibernateUtil.getEntityManager();

em.getTransaction().begin();

// Person anhand seinem Primärschclüssel laden.


Fk_Person person = em.find(Fk_Person.class, pk);

person.setAdresse(null);

// Person wieder speichern


em.getTransaction().commit();

Hibernate-Log
select fk_person0_.id as id2_1_, ... • Das Objekt person verliert wie
update Fk_Person set adresse_fk=null, ... gewünscht die Adresse.
• Aber die Adresse bleibt weiterhin
gespeichert.
• Lediglich der Fremdschlüssel auf die
Adresse wird auf NULL gesetzt.

tst.one2one.foreignkey

Alexander Kunkel 20.10.08 47


1:1 Beziehung unidirektional Delete Adresse
Delete Adresse
Testcode – Delete Adresse
EntityManager em = HibernateUtil.getEntityManager(); • Für eine korrekte Funktion müssen die
Operationen mit dem EntityManager
em.getTransaction().begin();
und die Beziehungen der Java-Objekte
// Person anhand seinem Primärschclüssel laden. konsistent sein.
Fk_Person person = em.find(Fk_Person.class, pk);

em.remove(person.getAdresse());

person.setAdresse(null);

// Person wieder speichern


em.getTransaction().commit();

Hibernate-Log
select fk_person0_.id as id2_1_, ...
update Fk_Person set adresse_fk=?, …
delete from Fk_Adresse where id=?

tst.one2one.foreignkey

Alexander Kunkel 20.10.08 48


1:n Beziehung unidirektional @OneToMany
ohne zusätzlicher Mappingtabelle @JoinColumn

Hier angeben, dass es sich um


class One2Many
Person eine 1:n-Beziehung handelt.
O2m_Person O2m_Telefon

- id: int - id: int


- nam e: String 0..* - num m er: S tring
Telefon
O2M_PERSON O2M_TELEFON

«colum n» «colum n»
ID ID
NAM E NUM M ER
PERSON_FK

Optional Spalte für den Fremdschlüssel. Ohne


Testcode - Insert @JoinColumn wird automatisch eine separate
Tabelle für die Beziehung benützt.

Hibernate-Log HSQLDB
insert into O2m_Person (id, name, ,..
call identity()
insert into O2m_Telefon (bemerkung, …
insert into O2m_Telefon (bemerkung, …
update O2m_Telefon set person_fk=? where nummer=?
update O2m_Telefon set person_fk=? where nummer=?

tst.one2many

Alexander Kunkel 20.10.08 49


1:n Beziehung unidirektional @OneToMany
mit zusätzlicher Beziehungstabelle @JoinColumn
class One2Many
Hier angeben, dass es sich um
O2m_Person O2m_Telefon
Person eine 1:n-Beziehung handelt.
- id : i nt - i d: i nt
0..* -
- nam e : S tring nu m m er: Stri ng
Telefon
O2M_PERSON O2M_TELEFON

«col um n» «col um n»
ID ID
NAM E NUM M ER

O2M_PERSON_O2M_TELEFON

« colu m n »
O 2M _PE RSO N_ID
T E LEFONNUM M ERN_ ID

Testcode - Insert Hibernate-Log


insert into O2m_Person (id, name, vorname) values …
call identity()
insert into O2m_Telefon (id, nummer, bemerkung) …
call identity()
insert into O2m_Telefon (id, nummer, bemerkung) …
call identity()
insert into O2m_Person_O2m_Telefon (O2m_Person_id, …
insert into O2m_Person_O2m_Telefon (O2m_Person_id, …

tst.one2many

Alexander Kunkel 20.10.08 50


1:n Beziehung unidirektional Delete Person
Delete Person

Testcode – Delete Person


EntityManager em = HibernateUtil.getEntityManager(); Das detacht Objekt person dem
em.getTransaction().begin(); neuen EntityManager übergeben.

person = em.merge(person); Das Objekt person löschen.


em.remove(person);

em.getTransaction().commit(); • Alternativ zum Aufruf von em.merge


könnte das Objekt mittels em.find ohne
anhängende Telefonnummern neu
Hibernate-Log geladen werden. Was allerdings für
select o2m_person0_.id as id17_1_, o2m_person0_.name ... em.remove zu einem zusätzlichen
update O2m_Telefon set myperson=null where myperson=?
delete from O2m_Telefon where id=? Select-Statement im Vergleich zu
delete from O2m_Telefon where id=? em.merge führt.
delete from O2m_Person where id=? • Sofern wie im Beispiel das transitive
Remove aktiv ist, werden bei Bedarf alle
Telefonnummern nachgeladen und
einzeln gelöscht.
• Ist das transitive Remove nicht aktiv, so
wird lediglich der Datensatz in
O2M_PERSON gelöscht und alle
Fremdschlüssel der dazugehörenden
Telefonnummern in O2M_TELEFON auf
NULL gesetzt.

tst.one2many

Alexander Kunkel 20.10.08 51


1:n Beziehung unidirektional Delete Telefon
Delete Telefon – 1. naiver Fehlversuch
Testcode – Delete Telefon
EntityManager em = HibernateUtil.getEntityManager();
Das detached Objekt person dem
em.getTransaction().begin(); neuen EntityManager übergeben.

person = em.merge(person);
List telefonNummern = person.getTelefonNummern(); • Das Objekt person verliert wie
gewünscht die Telefonnummer.
// Einfach eine Telefonnummer entfernen • Aber die Telefonnummer bleibt
telefonNummern.remove(0); weiterhin mit NULL als Fremdschlüssel
gespeichert.
// person wieder speichern
em.persist(person);
em.getTransaction().commit();

Hibernate-Log
select o2m_person0_.id as id17_0_, ...
select telefonnum0_.myperson as myperson1_, ...
update O2m_Telefon set myperson=null where myperson=? and id=?

tst.one2many

Alexander Kunkel 20.10.08 52


1:n Beziehung unidirektional Delete Telefon
Delete Telefon – 2. naiver Fehlversuch
Testcode – Delete Telefon
EntityManager em = HibernateUtil.getEntityManager();
em.getTransaction().begin(); Das detached Objekt person dem
neuen EntityManager übergeben.

person = em.merge(person);
List<O2m_Telefon> • Der Versuch wird mit einer Exception
telefonNummern = person.getTelefonNummern(); quittiert. Diese tritt auf, weil Hibernate
versucht, das möglicherweise
// Einfach eine Telefonnummer aus der Liste löschen geänderte Objekt person zu speichern.
em.remove(telefonNummern.get(0)); • Die Exception bemängelt den Versuch
eine gelöschte Entität zu speichern.
em.getTransaction().commit();
• Ursache für die Exception ist die
Telefonnummer, welche mit em.remove
gelöscht wurde, aber immer noch als
Hibernate-Log Objekt in person enthalten ist.
select o2m_person0_.id as id17_0_, ...
select telefonnum0_.myperson as myperson1_, ...
Exception in thread "main" javax.persistence.RollbackException:
Error while commiting the transaction
at ...
Caused by: org.hibernate.ObjectDeletedException:
deleted entity passed to persist: [tst.one2many.O2m_Telefon#<null>]
at ...

tst.one2many

Alexander Kunkel 20.10.08 53


1:n Beziehung unidirektional Delete Telefon
Delete Telefon
Testcode – Delete Telefon
EntityManager em = HibernateUtil.getEntityManager();
em.getTransaction().begin(); Das detacht Objekt person dem
neuen EntityManager übergeben.

person = em.merge(person);
List<O2m_Telefon> • Für eine korrekte Funktion müssen die
telefonNummern = person.getTelefonNummern(); Operationen mit dem EntityManager und
die Beziehungen der Java-Objekte
// Einfach eine Telefonnummer löschen und aus der konsistent sein.
// Liste der Telefonnummern entfernen. • In einem „echten“ Programm wird man
em.remove(telefonNummern.get(0)); den Code zur Pflege der Beziehungen in
telefonNummern.remove(0);
den Entities oder DAOs unterbringen.
em.getTransaction().commit();
• Für bidirektionale Beziehungen benötigt
man zusätzlich noch Code zur Pflege der
Rückwärtsreferenz.
Hibernate-Log
select o2m_person0_.id as id17_0_, ...
select telefonnum0_.myperson as myperson1_, ...
update O2m_Telefon set myperson=null where myperson=? and id=?
delete from O2m_Telefon where id=?

tst.one2many

Alexander Kunkel 20.10.08 54


1:n Beziehung unidirektional Delete Telefon
Delete Telefon Delete Orphan H!
Person – Delete Orphan
private List<O2m_Telefon> telefonNummern; Automatisches Löschen
verwaister Objekte, die aus einer
1:n Beziehung entfernt wurden.
@JoinColumn(name="myperson")
@OneToMany(cascade = CascadeType.ALL)
@Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
public List<O2m_Telefon> getTelefonNummern() {
[...]
}

Testcode – Delete Telefon Orphan


EntityManager em = HibernateUtil.getEntityManager();
Das detacht Objekt person dem
em.getTransaction().begin(); neuen EntityManager übergeben.

person = em.merge(person);
List<O2m_Telefon>
telefonNummern = person.getTelefonNummern();

// Einfach eine Telefonnummer aus der Beziehung


// entfernen.
telefonNummern.remove(0);

em.getTransaction().commit();

Hibernate-Log
select o2m_person0_.id as id23_1_,...
update O2m_Telefon set myperson=null where myperson=? and id=? tst.one2many
delete from O2m_Telefon where id=?

Alexander Kunkel 20.10.08 55


Lazy vs. Eager Loading LAZY
EAGER

• Strategie bzgl. dem Laden referenzierter Daten


– Eager: Gleich alles mitladen.
– Lazy: Erst dann laden, wenn ein Zugriff darauf erfolgt.
• Achtung!
– Gemäß EJB3 Spezifikation ist derzeit Lazy-Loading
optional.
– Lazy-Loading führt bei Hibernate zu Proxy-Objekten.
Probleme mit Objektidentitäten möglich.
– Lazy-Loading führt ggf. zu Detached Entities und später ggf.
zu Exceptions.
– Die „Tiefe“ des Mitladens ist beschränkt, aber einstellbar.
– Es gab Hibernate-Versionen vor 3.2.5 mit folgendem
Verhalten: Eager-Loading wirkt nicht bei einfachen Queries.
So erreicht man dann doch noch Eager Loading: Fetch Join,
em.refresh(…), em.find(…)
– Bei einem merge werden LAZY-Felder, die noch nicht
gefetcht wurden, auch nicht gemergt.
• Relevante Stellen
– Annotation bei Objektattributen und Beziehungen
– Fetchstrategie bei Abfragen
• Defaults
– Eager bei Objektattributen und 1:1 und n:1 Beziehungen.
– Lazy bei ?:n Beziehungen

Alexander Kunkel 20.10.08 56


Lazy vs. Eager Loading @Basic
Beispiele @OneToMany

Person
Abändern der Strategie
bei Attributen von
EAGER auf LAZY

Abändern der Strategie


bei einer 1:n Beziehung
von LAZY auf EAGER

SQL bei LAZY SQL bei EAGER

tst.lazyeager

Alexander Kunkel 20.10.08 57


Bidirektionale Beziehungen mappedBy
Objektreferenzen

Ergänzend zu unidirektionalen Beziehungen muss man ein wenig mehr tun:

• „mappedBy“ verweist auf die zuständige Seite der Beziehung


(owner). Dies erfolgt bei unidirektionalen Beziehungen implizit.
– Der Owner einer Seite legt fest, wo und wie der/die Fremdschlüssel der
Beziehung gespeichert wird/werden.  @JoinColumn, @JoinTable
– Bei 1:n und n:1 Beziehungen muss immer die n-Seite der owner sein.
Damit wird „mappedBy“ immer bei der 1-er Seite notiert.
• Pflege der Objektreferenzen
• Achtung!!! Bei Bidirektionalen Beziehungen können ungewollt
aufgrund der Defaultladestrategie „EAGER“ der Rückwärtsreferenz
ganze Objektnetze aus der Datenbank gelesen werden.

Alexander Kunkel 20.10.08 58


1:1 Beziehung bidirektional @OneToOne
mappedBy
class Bidirektional

Bi_Person Bi_Telefon BI_PERSON BI_TELEFON

- n a m e: S tri ng - i d: S tri ng « col um n » «co lu m n »


- vorn a m e: S tri ng 1 0..* - n u m m e r: S tri ng ID ID
- i d: L on g - b e m erku n g: S tri ng NA M E NUM M E R
- tel efo n Num m e rn : L ist<B i_ T el efo n > - p e rson : B i_ P erso n V ORNA M E B E M E RK UNG
- a d re sse : B i _A d re sse A DRE S S E _ID P E RS O N_ID

+ a d dT e le fo nNu m m er(B i_ T el efon ) : vo id 1


+ rem o ve T ele fo n Num m e r(B i_ T e le fon ) : vo id Bi_Adresse
BI_ADRESSE
- i d: L on g
0..1 - strasse: S tring «co lu m n »
- h a usn um m e r: S trin g ID
- p e rson : B i_ P erso n S T RA S S E
HA US NUM M E R

Person Adresse
private Bi_Adresse adresse; private Bi_Person person;

@OneToOne(cascade = CascadeType.ALL) @OneToOne(mappedBy="adresse")


public Bi_Adresse getAdresse() { public Bi_Person getPerson() {
return adresse;
return person;
}
}

public void setAdresse(Bi_Adresse adresse) {


public void setPerson(Bi_Person person) {
this.adresse = adresse;
} this.person = person;
}

tst.bidir

Alexander Kunkel 20.10.08 59


1:n Beziehung bidirektional @OneToMany
@ManyToOne

Person Telefon
private List<Bi_Telefon> telefonNummern; private Bi_Person person;

@OneToMany(cascade = CascadeType.ALL, mappedBy = "person") @ManyToOne


public List<Bi_Telefon> getTelefonNummern() { public Bi_Person getPerson() {
if (telefonNummern == null) {
return person;
telefonNummern = new ArrayList<Bi_Telefon>();
}
}
return telefonNummern;
public void setPerson(Bi_Person person) {
}
this.person = person;
public void setTelefonNummern(List<Bi_Telefon> nummern) { }
telefonNummern = nummern;
}

public void addTelefonNummer(Bi_Telefon telefon) {


telefon.setPerson(this);
getTelefonNummern().add(telefon); Pflege der Obejektreferenzen.
}

public void removeTelefonNummer(Bi_Telefon telefon) {


if (getTelefonNummern().remove(telefon)) {
telefon.setPerson(null);
}
else {
throw new IllegalStateException();
}
}
tst.bidir

Alexander Kunkel 20.10.08 60


1:1 Beziehung bidirektional Sonderfall
Foreign Key II
class ForeignKey2
• Durch die bidirektionale Beziehung ergibt sich die Möglichkeit einer
Person Adresse
weiteren Variante für 1:1 Beziehungen.
- na m e: S tri ng
1 0 ..1
- strasse: S tring • Fremdschlüssel wird beim Kindobjekt gespeichert
• Da es seitens der Datenbank wie eine 1:n Beziehung aussieht, ist es
sinnvoll die 1:1-Beziehung mittels unique Constraint zu schützen.
PERSON FK_ADRESSE
• Für JPA 1:1 ist eine Erweiterung für den Fall „Fremdschlüssel beim
« co lu m n»
ID
«co lu m n»
ID
Kindobjekt“ angekündigt.
NA M E S T RA S S E
FK _P E RS O N

Person Testcode

tst.one2one.foreignkeyreverse

Alexander Kunkel 20.10.08 61


Cascade (Transitive Persistenz) Desasterszenario

Person Telefon
private List<CA_Telefon> telefonNummern; private CA_Person person;

@OneToMany(cascade = CascadeType.ALL, mappedBy = "person") @ManyToOne(cascade = CascadeType.ALL)


public List<CA_Telefon> getTelefonNummern() { public CA_Person getPerson() {
[...]
[...]
}
}

public void setTelefonNummern(List<CA_Telefon> nummern) {


public void setPerson(CA_Person person) {
[...]
} [...]
}

Testcode Hibernate-Log
EntityManager em = HibernateUtil.getEntityManager(); select … from CA_Person … where …

CA_Person person = em.find(CA_Person.class, id); select … from CA_Telefon … where …

em.getTransaction().begin(); delete from CA_Telefon where id=?


CA_Telefon telefon = person.getTelefonNummern().get(0); delete from CA_Telefon where id=?
em.remove(telefon); delete from CA_Person where id=?
em.getTransaction().commit();

Das Löschen einer einzigen Telefonnummer


führt in Folge zur Löschung der Person und der
restlichen Telefonnummern der Person. tst.cascade

Alexander Kunkel 20.10.08 62


Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen

Alexander Kunkel 20.10.08 63


Vererbung SINGLE_TABLE
SINGLE_TABLE

• Eine Tabelle pro Objekthierarchie class SINGLE_TABLE

• Vorteile Parent SINGLE_TABLE


– Performanteste Variante. - attributeP: int «column»
– Einfach umzusetzen. ID
DISKRIMINAT OR
– Polymorphe Suche ist automatisch AT T RIBUT E_P
AT T RIBUT E_A
gegeben. AT T RIBUT E_B
– Es werden keine Joins benötigt.
ChildA ChildB
• Nachteile - attributeA: int - attributeB: int
– Platzverschwendung, da nicht alle
Attribute für jeden Typ relevant sind.
– Bei Einführung eines neuen Subtyps
muss die Tabelle angepasst werden.

Alexander Kunkel 20.10.08 64


Vererbung JOINED
JOINED

• Eine Tabelle für jede Klasse. class JOINED

• Vererbung wird als 1:1 Parent PARENT


Die 1:1 Beziehung zwische n

Beziehung zwischen den PARENT und CHILD_ ? wird


- attributeP: int «colum n»
m ittels gleichen Prim ary-Keys
ID

Tabellen abgebildet.
bewerkstelligt.
DISCRIM INAT OR
AT T RIBUT E_P

• Vorteile
– Klassen werden eins zu eins ChildA ChildB CHILD_A CHILD_B

abgebildet. - attributeA: int - attributeB: int «colum n» «colum n»

– Platz wird nicht verschwendet.


ID ID
AT T RIBUT E_A AT T RIBUT E_B

– Bei Einführung eines neuen


Subtyps müssen bestehende
Tabellen nicht geändert
werden.
• Nachteile
– Bereits bei einer Suche nach
einer konkreten Klasse muss
ein Join verwendet werden.
– Polymorphe Abfragen sind
komplex.

Alexander Kunkel 20.10.08 65


Vererbung TABLE_PER_CLASS
TABLE_PER_CLASS

class TABLE_PER_CLASS

• Eine Tabelle für jede konkrete CHILD_A CHILD_B


Parent
Klasse - attri buteP: i nt «col um n» «col um n»
ID ID
• Diese Strategie ist lt. JPA 1.0 A T T RIBUT E_P
A T T RIBUT E_A
AT T RIBUT E_P
AT T RIBUT E_B

optional.
• Vorteile ChildA ChildB

- attri buteA: i nt - attri buteB : i nt


– Für die Suche nach konkreten
Typen werden keine Joins
benötigt.
• Nachteile
– Gemeinsame Attribute von
Basistypen sind redundant.
– Pflege der Tabellen aufwendig.
– Polymorphe Abfragen sind
aufwendig. Mehrere Selects oder
Union.

Alexander Kunkel 20.10.08 66


Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen

Alexander Kunkel 20.10.08 67


Datenabfragen Übersicht
Übersicht

• Erste Anlaufstelle aller Abfragen ist


der EntityManager.
Datenabfragen
• Abfragen anhand des
Primärschlüssels unterstützt der
EntityManager Primary Key
EntityManager direkt.

• Alle sonstigen Abfragen erfolgen


Query-API JPA-QL mittels der Query-API und der
Datenabfragesprache JPA-QL.

• Die Query-API kennt 3 Querytypen


Query NativeQuery NamedQuery
– Query: JPA-QL Abfrage
– NativeQuery: Datenbankabhänige
SQL-Abfrage
– NamedQuery: In Annotationen
hinterlegte JPA-QL Abfrage

Alexander Kunkel 20.10.08 68


Datenabfragen .find(…)
Via EntityManager / Primärschlüssel .getReference(…)

.find(…)

Hibernate-Log
### Anhand Primärschlüssel vollständig laden
select q_person0_.id as id11_1_, q_person0_.name as name11_1_, …

.getReference(…)

Hibernate-Log Man beachte, dass der Select


erst bei Zugriff auf ein Attribut
### Anhand Primärschlüssel Referenz laden stattfindet
## Zugriff auf 'name'
select q_person0_.id as id11_1_, q_person0_.name as name11_1_, …

tst.query

Alexander Kunkel 20.10.08 69


Datenabfragen .createQuery
Query-API .setParameter

Erzeugen der verschiedenen Querytypen


EntityManager em = HibernateUtil.getEntityManager();

Query query = null;

query = em.createQuery(...);
query = em.createNativeQuery(...);
query = em.createNamedQuery(...);

Parametrisieren einer Query


select … where name=?1
// Nummerierte Parameter Die selbe Nummer bzw.
query.setParameter(nummer, ...); query.setParameter(1, „Duck“); der selbe Parametername
darf mehrmals in der
select … where name=:param Abfrage vorkommen.
// Benannte Parameter
query.setParameter(name, ...); query.setParameter(„param“, „Duck“);

Alexander Kunkel 20.10.08 70


.setFirstResult
Datenabfragen .setMaxResults
Query-API .setFlushMode

Einstellungen für die Paginierung


query.setFirstResult(startIndex);

query.setMaxResults(count);

FlushModeType.AUTO (default)

FlushMode Alle Änderungen an Entities im Persistenzkontext werden mit der


Datenbank vor der nächsten datenliefernden Abfrage
query.setFlushMode(FlushModeType.AUTO); synchronisiert.
Gewährleistet, dass alle Änderungen innerhalb einer Transaktion
sich auch in den Abfrageergebnissen niederschlagen.
query.setFlushMode(FlushModeType.COMMIT);

FlushModeType.COMMIT

Alle Änderungen an Entities im Persistenzkontext werden mit


der Datenbank spätestens bis zum Commit synchronisiert.
Ob Änderungen innerhalb einer Transaktion bei folgenden
Abfragen berücksichtigt sind, ist nicht vorhersagbar.

Alexander Kunkel 20.10.08 71


Datenabfragen .setHint(…)
Query-API

Produktspezifische Hinweise zur Einflussnahme


auf die Ausführung der Abfrage.
Hints (Hinweise) Kennt eine Implementierung einen gesetzten
Hinweis nicht, wird er ignoriert.
query.setHint(hintName, value);

H!

Achtung!
Nur in Ausnahmefällen verwenden.
Momentan ist jeder Hinweis
produktabhängig.

Alexander Kunkel 20.10.08 72


.executeUpdate()
Datenabfragen .getResultList()
Query-API .getSingleResult()

Update oder Delete ausführen Führt die Query aus und liefert
die Anzahl der von der Query
int count = query.executeUpdate(); aktualisierten bzw. gelöschten
Datensätze.
Mit einem Statement können
Achtung! mehrere Datensätze aktualisiert
Bulk Updates und Deletes wirken sich bei bzw. gelöscht werden.
Hibernate nicht auf Datenobjekte in  Bulk Updates / Deletes
einem optional aktiviertem Second Level
Cache aus.
Steht in der Query
irrtümlicher Weise die
falsche Abfrageart wie
Select ausführen Führt die Query aus und liefert beispielsweise ein Delete
eine Liste von Suchergebnissen. anstatt einem Select, dann
List list = query.getResultList(); wird eine
IllegalStateException
Object single = query.getSingleResult(); geworfen.

Diese Variante dann verwenden,


wenn man genau einen Treffer
für die Abfrage erwartet.
Sollte die Abfrage dennoch mehr
oder weniger als 1 Treffer
ergeben, so wird entweder eine
NonUniqueResultException oder
NoResultException geworfen.

Alexander Kunkel 20.10.08 73


Datenabfragen Method Chaining
Query-API

Zusammenstellung einer Query in mehreren Statements


Entitymanager em = HibernateUtil.getEntityManager();

Query q = em.createQuery("Select p from Q_Person p");


q.setParameter("name", "Duck");
List result = q.getResultList();

Zusammenstellung einer Query mittels Method Chaining


Entitymanager em = HibernateUtil.getEntityManager();

List result = em.createQuery("Select p from Q_Person p")


.setParameter("name", "Duck")
.getResultList();

Alexander Kunkel 20.10.08 74


Datenabfragen Select
Query-API, JPA-QL Left join fetch

Einfacher Select

Man beachte, dass trotz Eager-


Hibernate-Log Loading 2 Selects ausgeführt
### Einfacher Select werden
select q_person0_.id as id11_, q_person0_.name as name11_, …
select telefonnum0_.person_fk as person3_1_, …

Fetch Join

Hibernate-Log
### Einfacher Select als FETCH JOIN
select q_person0_.id as id11_0_, telefonnum1_.nummer as nummer12_1_, …

tst.query

Alexander Kunkel 20.10.08 75


Datenabfragen Select Count
Select Count, Paginierung Paginierung

Select Count

Hibernate-Log
### Select count
select count(q_telefon0_.nummer) as col_0_0_ from Q_Telefon q_telefon0_ where q_telefon0_.nummer like ?
## count=5

Paginierung

Hibernate-Log
### Paginierung
select top ? q_telefon0_.nummer as nummer12_, q_telefon0_.bemerkung as …
## count=3
select limit ? ? q_telefon0_.nummer as nummer12_, q_telefon0_.bemerkung as … tst.query
## count=2

Alexander Kunkel 20.10.08 76


Datenabfragen Statement Typen
JPA-QL

Syntax:
QL_statement ::= select_statement | update_statement | delete_statement

QL_statement

select_statement update_statement delete_statement

Alexander Kunkel 20.10.08 77


Datenabfragen select_statement
JPA-QL - select_statement

Syntax:
select_statement ::= select_clause from_clause [where_clause]
[groupby_clause] [having_clause] [orderby_clause]

select_statement

Bestimmt, welche Entitytypen


select_clause oder Werte selektiert werden.

Beinhaltet alle Deklarationen, auf


from_clause die sich andere Clauses beziehen.

Bedingungen, die die Ergebnis-


where_clause menge einschränken.

groupby_clause Gruppieren der Ergebnismenge.


optional

having_clause Auf die Gruppierung bezogene Filter.

orderby_clause Sortierung der Ergebnismenge festlegen.

Alexander Kunkel 20.10.08 78


Datenabfragen select_clause
JPA-QL - select_statement Objektselektion I

Einfachster Datenzugriffspfad
Selektiert eine Liste von Personen Q_Person mit Alias p.
EntityManager em = HibernateUtil.getEntityManager();
Query query = em.createQuery("select p from Q_Person p");
List result = query.getResultList();
In der from_clause werden die
for (Object object : result) { notwendigen Datenzugriffspfade
System.out.println(„ ### „ + object); definiert, auf die man sich in der
} select_clause bezieht.

Hibernate-Log
select q_person0_.id ...
select telefonnum0_.person_fk ...
### tst.query.Q_Person@f37a62[id=1,name=Duck, ...
select telefonnum0_.person_fk ...
### tst.query.Q_Person@e7eec9[id=2,name=Clever, ...

tst.query.Q_select_clause

Alexander Kunkel 20.10.08 79


Datenabfragen select_clause
JPA-QL - select_statement Objektselektion II

Selektiert eine Liste von Personen und Telefonnummern


query = em.createQuery("select p, t from Q_Person p, Q_Telefon t");
result = query.getResultList();

for (Object object : result) {


Ein wenig sinnlos. Soll lediglich
Object[] objects = (Object[])object;
zeigen, dass im Ergebnis
System.out.println(" ###[0] " + objects[0]);
mehrere Entitytypen geliefert
System.out.println(" ###[1] " + objects[1]);
werden können.
}

Hibernate-Log
select q_person0_.id ... from Q_Person q_person0_, Q_Telefon q_telefon1_
select telefonnum0_.person_fk ... from Q_Telefon telefonnum0_ where telefonnum0_.person_fk=?
###[0] tst.query.Q_Person@1497b1[id=1,name=Duck, ...
###[1] tst.query.Q_Telefon@1f31ad9[nummer=0177-1,bemerkung=mobil]
###[0] tst.query.Q_Person@1497b1[id=1,name=Duck, ... Die zahlreichen
###[1] tst.query.Q_Telefon@167acf2[nummer=0177-2,bemerkung=mobil] Datensätze sind das
###[0] tst.query.Q_Person@1497b1[id=1,name=Duck, ... Ergebnis des
###[1] tst.query.Q_Telefon@18b4ccb[nummer=0177-3,bemerkung=mobil] Kreuzprodukts von
###[0] tst.query.Q_Person@1497b1[id=1,name=Duck, ... Q_Person und Q_Telefon
###[1] tst.query.Q_Telefon@5ebac9[nummer=0177-4,bemerkung=mobil]
###[0] tst.query.Q_Person@1497b1[id=1,name=Duck, ...
###[1] tst.query.Q_Telefon@138ec91[nummer=0177-5,bemerkung=mobil] Zu jedem Personobjekt
###[0] tst.query.Q_Person@1497b1[id=1,name=Duck, ... werden die dazu
###[1] tst.query.Q_Telefon@335053[nummer=07152-23456,bemerkung=privat] gehörenden
###[0] tst.query.Q_Person@1497b1[id=1,name=Duck, ... Telefonnummern geladen.
###[1] tst.query.Q_Telefon@dea768[nummer=07152-2345667,bemerkung=privat]
select telefonnum0_.person_fk ... from Q_Telefon telefonnum0_ where telefonnum0_.person_fk=?
###[0] tst.query.Q_Person@1c0cd80[id=2,name=Clever, ...
###[1] tst.query.Q_Telefon@1f31ad9[nummer=0177-1,bemerkung=mobil] tst.query.Q_select_clause
...
Alexander Kunkel 20.10.08 80
Datenabfragen select_clause
JPA-QL - select_statement Diskrete Werte I

Selektiert Name und Vorname der Personen


Query query = em.createQuery("select p.name, p.vorname from Q_Person p");
List result = query.getResultList();

for (Object object : result) {


Object[] strings = (Object[])object;
System.out.println(" ###[0] " + strings[0]);
System.out.println(" ###[1] " + strings[1]);
}

Hibernate-Log
select q_person0_.name ... from Q_Person q_person0_
###[0] Duck
###[1] Donald
###[0] Clever
###[1] Class

tst.query.Q_select_clause

Alexander Kunkel 20.10.08 81


select_clause
Datenabfragen Diskrete Werte II
JPA-QL - select_statement Funktionen

Selektiert Name und Vorname der Personen und verbindet sie zu einem Text
query = em.createQuery("select concat(concat(p.name, ', '), p.vorname) from Q_Person p");
result = query.getResultList();

for (Object object : result) {


System.out.println(" ### " + object);
}

Hibernate-Log
select ((q_person0_.name||', ')||q_person0_.vorname) as col_0_0_ from Q_Person q_person0_
### Duck, Donald
### Clever, Class

tst.query.Q_select_clause

Alexander Kunkel 20.10.08 82


Datenabfragen select_clause
JPA-QL - select_statement Konstruktor

Beispiel nicht-Entity Klasse für einen Bericht oder Liste


public class Q_ReportRow {
private String name;
private String vorname;

public Q_ReportRow(String name, String vorname) {


this.name = name;
this.vorname = vorname;
}

public String toString() {


return name + ", " + vorname;
}
}

Erzeugt aus Name und Vorname der Personen Objekte eines nicht-Entity Typs
query = em.createQuery("select new tst.query.Q_ReportRow(p.name, p.vorname) from Q_Person p");
result = query.getResultList();

for (Object object : result) {


System.out.println(" ### " + object);
}

Hibernate-Log
select q_person0_.name as col_0_0_, q_person0_.vorname as col_1_0_ from Q_Person q_person0_
### Duck, Donald
### Clever, Class
tst.query.Q_select_clause

Alexander Kunkel 20.10.08 83


Datenabfragen select_clause
JPA-QL - select_statement Aggregatfunktionen

Anwendung einer einzigen Aggregatfunktionen auf eine Selektion


query = em.createQuery("select sum(p.geburtsjahr) from Q_Person p"); JPA-Aggregatfunktionen
Long summierteGeburtsjahre = (Long)query.getSingleResult(); •avg
System.out.println(" ### " + summierteGeburtsjahre); •count
•max
•min
Hibernate-Log •sum

Hibernate: select sum(q_person0_.geburtsjahr) as col_0_0_ from Q_Person q_person0_


### 3610

Anwendung mehrerer Aggregatfunktionen auf eine Selektion


query = em.createQuery("select min(p.geburtsjahr), count(p) from Q_Person p");
result = query.getResultList();

for (Object object : result) {


Object[] objects = (Object[])object;
System.out.println(" ###[0] " + objects[0]);
System.out.println(" ###[1] " + objects[1]);
}

Hibernate-Log
select min(q_person0_.geburtsjahr) as col_0_0_, count(q_person0_.id) as col_1_0_ from Q_Person q_person0_
###[0] 1798
###[1] 2

tst.query.Q_select_clause

Alexander Kunkel 20.10.08 84


Datenabfragen from_clause
JPA-QL - select_statement

Syntax:
from_clause ::= FROM identification_variable_declaration
{, {identification_variable_declaration | collection_member_declaration}}*

• Hier werden alle Datenzugriffspfade mit Einfachster Daten-


zugriffspfad: Q_Person
zugehöriger Variable definiert, die in
den anderen Clauses benützt werden. SELECT p FROM Q_Person p
 identification_variable_declaration ORDER BY p.name ASC

• Es muss mindestens ein


Datenzugriffspfad definiert sein.
abstract_schema_name: Q_Person
• Die Pfade können beliebig lang identification_variable: p

geschachtelt sein.
• Verschiedene Join-Typen:

Inner Join

Outer Join

Fetch Join

Impliziter Join bei n:1 und 1:1 in der
select_clause

Alexander Kunkel 20.10.08 85


Datenabfragen from_clause
identification_variable_declaration
JPA-QL - select_statement range_variable_declaration

Syntax:
identification_variable_declaration ::= range_variable_declaration { join |
fetch_join }*

range_variable_declaration ::= abstract_schema_name [AS]


identification_variable

• Das Schlüsselwort AS vor dem


Variablennamen ist optional
• abstract_schema_name entspricht einfach
dem Entityname

Einfachste Form SELECT p FROM Q_Person p

Ein vielleicht ungeschicktes Fallbeispiel, zeigt „theta-join“ – Impliziter Join
ohne definierte Objektbeziehung.
aber die mehrfache Verwendung des selben
abstract_schema_name  theta-join
SELECT p1 FROM Q_Person AS p1, Q_Person AS p2
WHERE p1.name < p2.name AND p2.vorname = ‚Donald‘

Hibernate-Log
select ...
from Q_Person q_person0_, Q_Person q_person1_
where q_person0_.name < q_person1_.name and q_person1_.name = 'Duck'
tst.query.Q_abstract_schema_name

Alexander Kunkel 20.10.08 86


Datenabfragen from_clause
identification_variable_declaration
JPA-QL - select_statement join

Syntax:
identification_variable_declaration ::= range_variable_declaration { join | fetch_join }*

join ::= join_spec join_association_path_expression [AS] identification_variable


fetch_join ::= join_spec FETCH join_association_path_expression

join_spec::= [ LEFT [OUTER] | INNER ] JOIN

• Für einen nicht Fetch Join wird wieder eine Variable vergeben, so dass sich
andere Clauses darauf beziehen können.
• Für einen Fetch Join wird keine Variable vergeben. Sie dienen lediglich als
Hinweis, die Beziehung EAGER zu laden.
• Der Datenpfad eines Joins kann ausschließlich auf einer Variable aufbauen,
die in der From-Clause definiert ist.
• Der Datenpfad eines Joins kann am Ende ein SingleValue oder eine
Collection adressieren. Die Zwischenstationen im Datenpfad können nur
SingleValues sein. Man kann quasi nicht über eine Collection
„hinweggehen“.

Alexander Kunkel 20.10.08 87


Datenabfragen from_clause
JPA-QL - select_statement Inner Join

Syntax:
identification_variable_declaration ::= range_variable_declaration { join | fetch_join }*

join ::= [INNER ] JOIN join_association_path_expression [AS] identification_variable

• Die Schlüsselwörter „INNER“ und „AS“ sind


optional.

Alexander Kunkel 20.10.08 88


Datenabfragen Fetchstrategien
Fetchstrategien bei 1:n Beziehungen

• Die Anzahl und die Art der DB-Statements variiert abhängig von der
Fetchstrategie.
• Der Zeitpunkt, an dem Daten von der DB angefordert werden, variiert
abhängig von der Fetchstrategie.
• Es folgen Beobachtungen am Beispiel der 1:n Beziehung: Person -> Telefon

Code zur Beobachtung der DB-Statements


1 Query query = em.createQuery("select distinct p from FE_Person p where p.name='Duck'");
2 List<FE_Person> personen = query.getResultList();
3 for (FE_Person person : personen) {
4 List<FE_Telefon> telefonNummern = person.getTelefonNummern();
5 for (FE_Telefon telefon : telefonNummern) {
6 System.out.println(telefon.getNummer());
7 }
8 }

tst.fetch

Alexander Kunkel 20.10.08 89


Datenabfragen Fetchstrategie
Fetchstrategien bei 1:n Beziehungen LAZY

Relevanter Codeausschnitt (Person)


Keine Besonderheit, da LAZY der
private List<FE_Telefon> telefonNummern;
Default für 1:n Beziehungen ist.
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "person_fk")
public List<FE_Telefon> getTelefonNummern() {
if (telefonNummern == null) {
telefonNummern = new ArrayList<FE_Telefon>();
}

return telefonNummern;
}

Hibernate-Log
4 List<FE_Telefon> telefonNummern = person.getTelefonNummern();
--> select ... from FE_Person fe_person0_
where fe_person0_.name='Duck' Jeweils beim Zugriff auf die erste
Telefonnummer einer Person.
„Load On Demand“
5 for (FE_Telefon telefon : telefonNummern) {
--> select ... from FE_Telefon telefonnum0_
where telefonnum0_.person_fk=?
select ... from FE_Telefon telefonnum0_
where telefonnum0_.person_fk=?

tst.fetch

Alexander Kunkel 20.10.08 90


Datenabfragen Fetchstrategie
Fetchstrategien bei 1:n Beziehungen EAGER

Relevanter Codeausschnitt (Person)


private List<FE_Telefon> telefonNummern;

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)


@JoinColumn(name = "person_fk")
public List<FE_Telefon> getTelefonNummern() {
if (telefonNummern == null) {
telefonNummern = new ArrayList<FE_Telefon>();
}

return telefonNummern;
}

Hibernate-Log
4 List<FE_Telefon> telefonNummern = person.getTelefonNummern();
--> select ... from FE_Person fe_person0_ Die selben DB-Statements wie bei
where fe_person0_.name='Duck' Lazy. Allerdings werden alle Daten
select ... from FE_Telefon telefonnum0_ sofort mit der Ausführung der
where telefonnum0_.person_fk=? Query geladen.
select ... from FE_Telefon telefonnum0_
where telefonnum0_.person_fk=?

tst.fetch

Alexander Kunkel 20.10.08 91


Datenabfragen Fetchstrategie
Fetchstrategien bei 1:n Beziehungen SUBSELECT H!

Relevanter Codeausschnitt (Person)


private List<FE_Telefon> telefonNummern;

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "person_fk")
@org.hibernate.annotations.Fetch(FetchMode.SUBSELECT)
public List<FE_Telefon> getTelefonNummern() {
if (telefonNummern == null) {
telefonNummern = new ArrayList<FE_Telefon>();
}

return telefonNummern;
}

Hibernate-Log
4 List<FE_Telefon> telefonNummern = person.getTelefonNummern();
--> select ... from FE_Person fe_person0 Ein einziger Select für alle
Telefonnummern.
where fe_person0_.name='Duck'

5 for (FE_Telefon telefon : telefonNummern) {


--> select ... where telefonnum0_.person_fk
in (select fe_person0_.id from FE_Person fe_person0_
where fe_person0_.name='Duck')

Achtung!
Meine Experimente mit FetchMode.JOIN und FetchMode.SELECT haben im Widerspruch zu deren
Dokumentation keine neue Fetchstrategien gezeigt. Das könnte an der Hibernateversion oder meiner
Verwendung gelegen haben.
tst.fetch

Alexander Kunkel 20.10.08 92


Datenabfragen Fetchstrategie
Fetchstrategien bei 1:n Beziehungen JOIN FETCH

Relevanter Codeausschnitt (Person)


Genauso wie bei LAZY.
private List<FE_Telefon> telefonNummern;

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "person_fk")
public List<FE_Telefon> getTelefonNummern() {
if (telefonNummern == null) {
telefonNummern = new ArrayList<FE_Telefon>();
}

return telefonNummern;
}

„distinct“ wird aufgrund des


Relevanter Codeausschnitt (Query) Kreuzprodukts durch den Join
notwendig.
1 Query query = em.createQuery("select distinct p
from FE_Person p left join fetch p.telefonNummern
where p.name='Duck'");

Hibernate-Log
4 List<FE_Telefon> telefonNummern = person.getTelefonNummern();
--> select distinct ... from FE_Person fe_person0_ Ein einziger Select für alle
Personen und Telefonnummern.
left outer join FE_Telefon telefonnum1
on fe_person0_.id=telefonnum1_.person_fk
where fe_person0_.name='Duck'

tst.fetch

Alexander Kunkel 20.10.08 93


Datenabfragen Fetchstrategien
Fetchstrategien bei 1:n Beziehungen Vergleich

• LAZY und EAGER führen zu naiven Datenbankabfragen.


• EAGER verhindert dabei wenigstens das Problem mit „Detached“
• LAZY könnte eine Stärke zugebilligt werden, wenn nur selten auf
Telefonnummern zugegriffen wird, oder die Anzahl der Personen klein ist.
• Sobald optimiert werden muss bleiben nur SUBSELECT und JOIN FETCH.
• Hier muss man zwischen JPA Standard und Hibernate proprietär abwägen
und zwischen 1 Select mit Kreuzprodukt oder 2 Selects mit Subselect.

Alexander Kunkel 20.10.08 94


Datenabfragen Null==„“ ?!
ORACLE: null==„“

Achtung!
Nicht immer stehen die Daten so in der Datenbank, wie man sie ursprünglich eingefügt hatte.
Das macht sich mindestens dann unangenehm bemerkbar, wenn man auf Basis von beispielsweise MySQL entwickelt und dann für
ORACLE ausliefert.
Die Kapselung durch JPA oder Hibernate ändert nichts an der unterschiedlichen Speicherung.

HSQL speichert Strings wie


zuvor eingefügt.
• Fälle (HSQL) Entsprechende Abfragen
liefern auch das erwartete
Ergebnis.

ORACLE speichert Leerstrings als NULL.


Die Abfrage: select n from NullOrEmpty as n where n.string='‚
• Fälle (Oracle) liefert bei ORACLE keine Treffer.
Die Abfrage: select n from NullOrEmpty as n where n.string is null
liefert bei ORACLE keinen Treffer.

tst.oracle.NullOrEmpty

Alexander Kunkel 20.10.08 95


Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen

Alexander Kunkel 20.10.08 96


Sortieren @OrderBy
Datenbankseitiges Sortieren

Person Telefon
private List<S_Telefon> telefonNummern; @Entity
public class S_Telefon {
@OneToMany(cascade = CascadeType.ALL, ...
fetch = FetchType.EAGER) // Hier gibt es nichts besonderes
@JoinColumn(name = "person_fk") ...
@OrderBy(value = "nummer DESC") }
public List<S_Telefon> getTelefonNummern() {
...
}

Testcode
EntityManager em = HibernateUtil.getEntityManager();
person = em.find(S_Person.class, rememberPrimaryKey);

Hibernate-Log
select s_person0_.id as id6_1_, ...
from S_Person s_person0_ left outer join S_Telefon telefonnum1_ on s_person0_.id=telefonnum1_.person_fk
where s_person0_.id=?
order by telefonnum1_.nummer DESC

tst.sort

Alexander Kunkel 20.10.08 97


Sortieren Join fetch, order by
Datenbankseitiges Sortieren

Person Telefon
private List<S_Telefon> telefonNummern; @Entity
public class S_Telefon {
@OneToMany(cascade = CascadeType.ALL, ...
fetch = FetchType.EAGER) // Hier gibt es nichts besonderes
@JoinColumn(name = "person_fk") ...
@OrderBy(value = "nummer DESC") }
public List<S_Telefon> getTelefonNummern() {
...
}

Testcode
EntityManager em = HibernateUtil.getEntityManager();
Query q = em.createQuery("Select p from S_Person p left join fetch p.telefonNummern
order by p.name asc");
List result = q.getResultList();

Hibernate-Log
select s_person0_.id as id6_0_, ...
from S_Person s_person0_ left outer join S_Telefon telefonnum1_ on s_person0_.id=telefonnum1_.person_fk
order by s_person0_.name asc, telefonnum1_.nummer DESC

tst.sort

Alexander Kunkel 20.10.08 98


Sortieren @Sort
Sortieren im Speicher H!
Person Telefon
private SortedSet<S_Telefon> telefonNummern; @Entity
public class S_Telefon {
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) ...
@JoinColumn(name = "person_fk") // Hier gibt es nichts besonderes
@Sort(type=SortType.COMPARATOR,comparator=TelefonComparator.class) ...
public SortedSet<S_Telefon> getTelefonNummern() { }
...
}

TelefonComparator
public class TelefonComparator implements Comparator<S_Telefon> {
public int compare(S_Telefon o1, S_Telefon o2) {
...
}
}

Testcode
EntityManager em = HibernateUtil.getEntityManager();
person = em.find(S_Person.class, rememberPrimaryKey); Keine OrderBy-Clause, aber
dennoch eine sortierte
Telefonliste
Hibernate-Log
select s_person0_.id as id6_1_, ...
from S_Person s_person0_ left outer join S_Telefon telefonnum1_ on s_person0_.id=telefonnum1_.person_fk tst.sort
where s_person0_.id=?

Alexander Kunkel 20.10.08 99


Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen

Alexander Kunkel 20.10.08 100


Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen

Alexander Kunkel 20.10.08 101


Optimistisches Verfahren @Version

Person
Zusätzliches Attribut, um • Für @Version können folgende
Information zur Objektversion
aufzunehmen. Typen eingesetzt werden:
– int, Integer
– short, Short
– long, Long
Testcode
– Timestamp

Hibernate-Log
insert into L_Person (id, version, name) values …
call identity()
select l_person0_.id as id7_0_, l_person0_.version as …
select l_person0_.id as id7_0_, l_person0_.version as …
update L_Person set version=?, name=? where version=? …
update L_Person set version=?, name=? where version=? …
… Row was updated or deleted by another transaction

javax.persistence.RollbackException
caused by … tst.locking
javax.persistence.OptimisticLockException

Alexander Kunkel 20.10.08 102


Pessimistisches Sperren

• Lt. JPA Spezifikation gibt es ausschließlich optimistisches Sperren.


• EntityManager.lock erweckt den Eindruck, als gäbe es pessimistische Sperren.
– Worin besteht der Unterschied?
– READ verhindert non-repeatable-read dadurch, dass eine beteiligte Transaktion
abgebrochen wird.
• Ergänzend zur JPA kennt Hibernate pessimistisches Sperren

Alexander Kunkel 20.10.08 103


Pessimistisches Sperren
Beispiel mit Hibernate H!

• PessimisticTest & Testcode


PessimisticTransaction EntityManager em = HibernateUtil.getEntityManager();

Testklasse zum Beobachten des em.getTransaction().begin();
Sperrverhaltens zweier Transaktionen HibernateEntityManager
mittels Debugger. hem = (HibernateEntityManager) em;
L_Person personA = (L_Person) hem.getSession()

Breakpoint an den Anfang von .get(L_Person.class, id,
‚Transaction.run‘ stellen und LockMode.UPGRADE);
Testprogramm starten.  Beide
Transaktionen bleiben im Debugger personA.setName(name);
em.persist(personA);
stehen.

Im Einzelschrittmodus des Debuggers em.getTransaction().commit();
nach belieben zwischen den
Transaktionen wechseln und
beobachten was passiert.

tst.locking

Alexander Kunkel 20.10.08 104


Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen

Alexander Kunkel 20.10.08 105


Designempfehlungen I

• Halten Sie das Klassenmodell so einfach wie möglich.


– Ein komplexes Modell macht auch mit einem OR-Mapper reichlich
Schwierigkeiten.
– Zur Vereinfachung des Designs sollte an Modulgrenzen auf eine modellierte
Beziehung und der damit in der Datenbank verbundenen Fremdschlüssel
verzichtet werden.
– Sobald im Modell zu den hierarchischen Beziehungen noch Querbeziehungen
hinzukommen, wird es in der Regel reichlich kompliziert, die Beziehungen korrekt
zu pflegen. Ein Ausweg kann eine Facade sein, die die Pflege der Beziehungen
komplett übernimmt.
• Erstellen Sie Richtlinien, wie Datenobjekte auszusehen haben.
– Ansonsten können bald keine Aussagen darüber gemacht werden, wie sich die
Objekte in verschiedenen Situationen verhalten.
– Entwickler können ihr erworbenes Wissen aus anderen Modulen mit
Datenobjekten nicht auf ihnen unbekannte Module übertragen.
– Bei unterschiedlich gestalteten Datenklassen sind eigentlich Unit-Tests für alle
Datenklassen und Situationen notwendig. Haben Sie Standards, so müssen nur
noch die Ausnahmesituationen umfassend durch Unit-Tests abgesichert werden.
Für die Standardsituationen genügen eigentlich Stichprobentests.
– Ohne detaillierte Vorgaben und Muster wird die Persistenzanbindung unwartbar.
– Die Verwendung von @Temporal genau festlegen und bei Bedarf die Richtlinie
anpassen. Verlassen Sie sich insbesondere nicht auf das Defaultmapping.

Alexander Kunkel 20.10.08 106


Designempfehlungen II

• Keine Cascades bei Rückwärtsreferenzen.


– Denken Sie an das Desasterszenario aus diesem Foliensatz.
– Sollten Sie an gut begründeten Stellen eine Ausnahme machen müssen, dann
begründen Sie dies an der betreffenden Stelle im Javadoc!
• Designen Sie alle 1:n Beziehungen zunächst LAZY.
– 1:1 Beziehungen werden ohnehin EAGER geladen. Das gilt übrigens auch bei
Rückwärtsreferenzen, an die Sie möglicherweise gegen die Empfehlung zuvor
ein Cascade für merge oder persist annotiert haben.
– Wenn EAGER geladen werden soll, dann verwenden Sie Join Fetch in der
betreffenden Abfrage.
– Sollte sich dennoch FetchType.EAGER für die Beziehung aufdrängen, dann
dokumentieren Sie den Grund dafür im Javadoc!

Alexander Kunkel 20.10.08 107

You might also like