Folien

Werbung
Java Persistenz mit
JPA und Hibernate
Michael Plöd - Senacor Technologies AG
Michael Plöd
• Partner bei Senacor
Technologies AG
• Hibernate Erfahrung seit
2002
• [email protected]
Ihr Hintergrund
• Erfahrung mit Java?
• Erfahrung mit Java Persistenz?
• Tägliche Projektarbeit?
• Einsatz von Hibernate im Projekt?
• Ihre Erwartungen an die Schulung?
Agenda
Zeit
Thema
08:00 - 09:00
Einleitung und theoretische Grundlagen
09:00 - 09:30
Hibernate im Überblick
09:45 - 10:30
Mapping von Klassen
10:30 - 11:15
Arbeiten mit Hibernate
11:15 - 11:45
Queries
Datenbank
Java Anwendung
<<TABLE>>
FOTOS
ID <PK>
NAME
...
Foto
<<TABLE>>
FOTOS_IN_GALERIEN
PIC_ID <PK><FK>
GAL_ID <PK><FK>
0..*
Object-Relational
Impedance Mismatch
0..*
Galerie
<<TABLE>>
GALERIEN
ID <PK>
TITEL
...
Impedance Mismatch
Datenbank
Identität
Granularität
Vererbung
Navigation
Assoziationen
OO-Software
• Primärschlüssel
• Objekt-Identität
• Objekt-Gleichheit
• Identity vs Equality
• Erstellung eigener
• Eigene Typen können
• Vererbung nur durch
• Vererbung ist Bestandteil
• Navigation in jeder
• Navigation wird explizit
• Navigation über Joins
• Join-Tabellen bei n:m
• To-One Relationen (Pointer)
• To-Many Relationen
Datentypen sehr
proprietär
Datenmodell
Richtung ohne
Einschränkbarkeit möglich
jederzeit erstellt werden
von OO-Software
deklariert
• Gerichtete Navigation
somit möglich
(Collections)
Java Persistenz
Feature-Set
Hibernate
• JDBC ist Java SE Standard
JPA 2.0
• iBATIS und Spring JDBC Template
sind JDBC Frameworks
• JPA 1, JPA 2 und Hibernate sind ORMapper
JPA 1.0
iBATIS
JDBC-Template
JDBC
Code Menge / Komplexität
JDBC
ist doch einfach
Einfacher Select mit
JDBC
List<User> users = new ArrayList<User>();
Class.forName(”org.postgresql.Driver”);
Connection con = DriverManager.getConnection(url, username, password);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(”SELECT * FROM USERS”);
while(rs.hasNext()) {
User u = new User();
u.setName(rs.getString(1));
users.add(u);
}
rs.close();
stmt.close();
con.close();
!
Exception
Handling
fehlt
Einfacher Select mit
JDBC
try {
List<User> users = new ArrayList<User>();
Class.forName(”org.postgresql.Driver”);
Connection con = DriverManager.getConnection(url, username, password);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(”SELECT * FROM USERS”);
while(rs.hasNext()) {
User u = new User();
u.setName(rs.getString(1));
users.add(u);
}
rs.close();
stmt.close();
con.close();
} catch (SQLException e) {
//Exception Handling
}
Resource
Leaks
„Einfacher“ Select mit JDBC
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
List<User> users = new ArrayList<User>();
Class.forName(”org.postgresql.Driver”);
con = DriverManager.getConnection(url, username, password);
stmt = con.createStatement();
rs = stmt.executeQuery(”SELECT * FROM USERS”);
while(rs.hasNext()) {
User u = new User();
u.setName(rs.getString(1));
users.add(u);
}
rs.close();
stmt.close();
con.close();
} catch (SQLException e) {
//Exception Handling
} finally {
if(rs != null) {
try {rs.close();} catch (SQLException e) {...};
}
if(stmt != null) {
try {stmt.close();} catch (SQLException e) {...};
}
if(con != null) {
try {con.close();} catch (SQLException e) {...};
}
}
JDBC Frameworks
Eigenschaften
Pro
Contra
Beispiele
• Adressieren Komplexität von JDBC
• Bieten wiederverwendbare Komponenten
• Einfache Typ Mappings (z.B. RowMapper)
• Einfach zu benutzen
• Wenige Fallstricke
• Auch für extrem komplexe Legacy Datenbanken geeignet
• Begrenztes Caching
• Beschränkte Typ Mappings
• Zahlreiche manuelle Aufrufe
• Spring JDBCTemplate
• iBATIS
OR-Mapper
Eigenschaften
Pro
Contra
Beispiele
• Adressieren Komplexität von JDBC
• Komplette Typ Mappings
• Ausgefeiltes Caching System
• Transaktionales Write-Behind
• Einfach zu benutzen
• Caching
• Code-Menge
• Manchmal komplex
• Schwächen bei schlecht entworfenen Legacy Datenbanken
• Hibernate
• EclipseLink
• Open JPA
Aufbau eines ORMappers
Configuration
Factory
2nd Level
Cache
Persistence
Context
1st Level Cache
Abbildung auf
Datenbank
Adresse
Person
Konto
XML
PERSONEN
Mapping
public class Person {
private Adresse adresse;
private Set<Konto> konten;
}
//... getter und setter
Annotationen
KONTEN
Aufbau von JPA
Persistence
EntityManager
Factory
2nd Level Cache
EntityManager
1st Level Cache
Aufbau von Hibernate
Configuration
SessionFactory
2nd Level Cache
Session
1st Level Cache
Hibernate‘s Module
ORM
EntityManager
• Kern von Hibernate
• OR-Mapping Engine
• XML- und Annotation-Mappings
• JPA Implementierung auf Basis von Core und Annotations
Shards
• Unterstützung für verteilte Datenbestände
• Von Google
Search
• Mächtige Volltext-Suche
• Basiert auf Lucene
OGM
• Hibernate für NoSQL Stores
• DDL Generierung
Bean Validation
• Validierung
• DDL Generierung
Transaktionales
Write-Behind
begin
commit
Transaktion
open
Session
saves: 1
saves: 1
updates: 1
close
session.save(person)
Logik
session.update(konto)
DATENBANK
INSERT
UPDATE
Agenda
Zeit
Thema
08:00 - 09:00
Einleitung und theoretische Grundlagen
09:00 - 09:30
JPA im Überblick
09:45 - 10:30
Mapping von Klassen
10:30 - 11:15
Arbeiten mit Hibernate
11:15 - 11:45
Queries
JPA im Überblick
Aufbau von JPA
Persistence
EntityManager
Factory
2nd Level Cache
EntityManager
1st Level Cache
Einfaches Mapping
@Entity
public class Person {
@Id
private long id;
private String vorname;
private String nachname;
private long getId() {
return id;
}
private void setId(long id) {
this.id = id;
}
//... weitere getter und setter
//... equals und hashCode
}
Person
ID NUMBER <PK>
VORNAME VARCHAR
NACHNAME VARCHAR
Konfiguration
META-INF/persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="test">
<class>com.senacor.schulung.hibernate.domain.Person</class>
<properties>
<property name="javax.persistence.jdbc.driver"
value="org.hsqldb.jdbcDriver" />
<property name="javax.persistence.jdbc.url"
value="jdbc:hsqldb:hsql://localhost/testdb" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="hibernate.dialect"
value="org.hibernate.dialect.HSQLDialect"/>
</properties>
</persistence-unit>
</persistence>
Initialisierung
EntityManagerFactory
Aus persistence.xml
wird
einmalig
EntityManagerFactory
erstellt
EntityManagerFactory entityManagerFactory =
Persistence.createEntityManagerFactory( "test" );
Arbeiten mit JPA
1. EntityManager erstellen
2. Transaktion starten
3. Operationen durchführen
4. Transaktion committen
5. EntityManager schließen
EntityManager e = emf.createEntityManager()
User u = new User();
u.setName("TestUser");
e.getTransaction().begin();
e.persist(u);
e.getTransaction().commit();
e.close();
Agenda
Zeit
Thema
08:00 - 09:00
Einleitung und theoretische Grundlagen
09:00 - 09:30
JPA im Überblick
09:45 - 10:30
Mapping von Klassen
10:30 - 11:15
Arbeiten mit Hibernate
11:15 - 11:45
Queries
Einfache Mappings
• Domänenmodellierung
• Entitäten
• Attribute
• Primärschlüssel
• Value Objects
• Assoziationen
Identifikation von Entitäten
und Value Objects
• Ein gutes Domänenmodell unterscheidet
zwischen Entitäten und Value Objects
• Des Weiteren ist die Navigation im Modell
klar gegliedert und folgt einheitlichen
Richtlinien
• Fokus des Modells sollte primär
Fachlichkeit sein
Entitäten
besitzen eine
konstante
Identität
und einen
eigenen
Lebenszyklus
★ Identität durch Attribute bestimmt
★ Kein eigener Lebenszyklus
Value Objects
Buchtip:
Domain Driven Design
von
Eric Evans
Addison Wesley Verlag
ISBN: 0321125215
Mapping von Entitäten
• Eine Entität muss mit @Entity gemappt
werden
• Eine Entität muss einen mit @Id
annotierten Primärschlüssel haben
• Eine Entität muss einen No-Arg
Constructor haben
Beispiel
@Entity
@Table(name=“PERSONEN“)
public class Person {
@Id
private long id;
private String vorname;
private String nachname;
private long getId() {
return id;
}
private void setId(long id) {
this.id = id;
}
//... weitere getter und setter
//... equals und hashCode
}
PERSONEN
ID NUMBER <PK>
VORNAME VARCHAR
NACHNAME VARCHAR
PrimärschlüsselMapping
@Id
private long id;
• Default: manuell gesetzter Primärschlüssel
Generierungs Strategien
Name
Description
increment
Generiert long, int, short basierend auf Tabelle. Nicht in Cluster!
identity
Identity Columns in DB2, MySQL, MS SQL Server, Sybase, HSQL
sequence
Sequence in DB2, PostgreSQL, Oracle, SAP DB, McKoi, Interbase
hilo
HI/LO Algorithmus basierend auf Tabelle
seqhil
HI/LO Algorithmus basierend auf Sequence
uuid
128-bit UUID Algorithmus, auf Netzwerk eingeschränkt
guid
GUID String in MS SQL Server und MySQL
native
Identity, Sequence oder hilo je nach Datenbank
assigned
ID wird von Applikation zugewiesen
select
Zieht ID, die durch Trigger vergeben wird
foreign
Zieht ID, die durch anderes Objekt vergeben wird
sequence-identity
Spezialisierte Sequence Generierung basierend auf JDBC 3
PrimärschlüsselMapping Beispiele
@Entity
@javax.persistence.SequenceGenerator (
name=“SEQ“,
sequenceName=“seq_person“)
public class Person {
@Id
@GeneratedValue(
strategy=GenerationType.SEQUENCE,
generator=“SEQ“)
private long id;
PrimärschlüsselMapping Beispiele
@Entity
@javax.persistence.TableGenerator (
name="TAB_GEN",
table="GENERATOR_TABLE",
pkColumnName = "key",
valueColumnName = "hi",
pkColumnValue = "PERS",
allocationSize = "20")
public class Person {
@Id
@GeneratedValue(
strategy=GenerationType.TABLE,
generator=“TAB_GEN“)
private long id;
Mapping von Attributen
• Platzierung von Annotationen auf Attribut
oder Getter-Ebene möglich
• Platzierung entscheidet über Zugriff (via
Reflection oder über Getter)
• Default: jedes nicht annotierte Attribut ist
persistent
• Alle gängigen Typen werden unterstützt
Mapping von Attributen
• @Column für Spaltenkonfiguration
• Sondermappings für Date und (B)LOB
Support
• @Transient für nicht-persistente Attribute
Attribute-Mapping
Beispiele
@Entity
public class Person {
@Id
@Column(name=“PERS_ID“)
private long id;
@Column(name=“PERS_VORNAME“, nullable=“false“)
private String vorname;
@Column(name=“PERS_NACHNAME“, nullable=“false“)
private String nachname;
@Temporal(value=TemporalType.DATE)
@Column(name=“PERS_GEBURTSTAG)
private Date geburtstag;
@Transient
private short alter;
...
}
Mapping von Value
Objects
• Beispiel: Person ist Entität mit konstanter
ID, Adresse ist einfaches Objekt ohne
konstante ID
Person
id : long
vorname : String
nachname : String
Adresse
strasse : String
plz : String
stadt : String
Mapping von Value
Objects
• Notation Value Object ist in Java nicht
vorgesehen
• Value Object ist einfaches Attribut von
Entität
• Value Object hat keinen eigenen
Lebenszyklus
• Lebenszyklus wird von Entität gesteuert
Value Object - Mapping
@Embeddable
public class Adresse {
@Column(name=“ADR_STRASSE“)
private String strasse;
@Column(name=“ADR_PLZ“)
private String plz;
@Column(name=“ADR_STADT“)
private String stadt;
... getter und setter
PERSON
ID NUMBER <PK>
VORNAME VARCHAR
}
NACHNAME VARCHAR
@Entity
public class Person {
@Id
private long id;
private String vorname;
private String nachname;
ADR_STRASSE VARCHAR
@Embedded
private Adresse anschrift
...
}
ADR_PLZ VARCHAR
ADR_STADT VARCHAR
Value Object - Mapping
@Entity
public class Person {
...
@Embedded
@AttributeOverrides ({
@AttributeOverride(name=“strasse“,
column=@Column(name=“PERS_ADR_STRASSE“)),
@AttributeOverride(name=“plz“,
column=@Column(name=“PERS_ADR_PLZ“)),
@AttributeOverride(name=“stadt“,
column=@Column(name=“PERS_ADR_STADT“))
})
private Adresse anschrift
...
}
@Embeddable
public class Adresse {
@Column(name=“ADR_STRASSE“)
private String strasse;
@Column(name=“ADR_PLZ“)
private String plz;
@Column(name=“ADR_STADT“)
private String stadt;
... getter und setter
}
Mapping von Relationen
• JPA unterstützt folgende Relationen
• One-To-One
• One-To-Many
• Many-To-Many
• Unterscheidung zwischen uni- und
bidirektionalen Relationen
• Unterstützung für List, Map,Value Objects
Relationen Teil 1
• Unidirektionale Relation geht nur in eine
Richtung und ist daher einfach und
empfehlenswert
• JPA verwaltet bi-direktionale Relationen
nicht von selbst (siehe fortgeschrittene
Mappings)
• Best Practice: Aggregate
Aggregate
gruppieren
Entitäten
<Entität>
<Entität
>
<Root
Entität>
Selbstauskunft
<Root
Entität>
<Entität
>
Kreditantrag
<Entität>
<Entität>
<Entität>
TilgungsDetail
Darlehen
Kunde
Adresse
Many-To-One
Person
Konto
id : long
vorname : String
nachname : String
nummer : String
blz : String
PERSON
ID NUMBER <PK>
VORNAME VARCHAR
NACHNAME VARCHAR
KONTO_ID NUMBER <FK>
KONTO
ID NUMBER <PK>
NUMMER NUMBER
BLZ VARCHAR
Many-To-One
@Entity
public class Person {
@Id
private long id;
private String vorname;
private String nachname;
@ManyToOne
private Konto konto
...
}
@Entity
public class Konto {
@Id
private long id;
private Integer nummer;
private String blz;
...
}
Many-To-One
@Entity
public class Person {
@Id
private long id;
private String vorname;
private String nachname;
@ManyToOne
@JoinColumn(name=“PERS_KTO_ID“, nullable=false)
private Konto konto
...
}
@Entity
public class Konto {
@Id
private long id;
private Integer nummer;
private String blz;
...
}
One-To-Many
Person
Konto
id : long
vorname : String
nachname : String
nummer : String
blz : String
PERSON
ID NUMBER <PK>
VORNAME VARCHAR
NACHNAME VARCHAR
KONTO
ID NUMBER <PK>
NUMMER NUMBER
BLZ VARCHAR
PERSON_ID NUMBER <FK>
One-To-Many
@Entity
public class Person {
@Id
private long id;
private String vorname;
private String nachname;
Ungeordnete
Collection
@OneToMany
private Set<Konto> konten = new HashSet<Konto>();
...
}
@Entity
public class Konto {
@Id
private long id;
private Integer nummer;
private String blz;
...
}
One-To-Many
@Entity
public class Person {
@Id
private long id;
private String vorname;
private String nachname;
@OneToMany
@JoinColumn(name=“KTO_PERSON_ID“)
private Set<Konto> konten = new HashSet<Konto>
...
}
@Entity
public class Konto {
@Id
private long id;
private Integer nummer;
private String blz;
...
}
One-To-Many mit List
@Entity
public class Person {
@Id
private long id;
private String vorname;
private String nachname;
Spalte wird benötigt,
die Reihenfolge
persistiert
@OneToMany
@JoinColumn(name=“KTO_PERSON_ID“)
@IndexColumn(name=“KTO_ORDER“)
private List<Konto> konten = new ArrayList<Konto>();
...
}
KONTO
ID NUMBER <PK>
NUMMER NUMBER
BLZ VARCHAR
KTO_PERSON_ID NUMBER <FK>
KTO_ORDER NUMBER
One-To-One
Shared Primary Key
Person
Konto
id : long
vorname : String
nachname : String
nummer : String
blz : String
PERSON
KONTO
ID NUMBER <PK>
ID NUMBER <PK><FK>
VORNAME VARCHAR
NUMMER NUMBER
NACHNAME VARCHAR
BLZ VARCHAR
One-To-One
Shared Primary Key
@Entity
public class Person {
@Id
private long id;
...
@OneToOne
@PrimaryKeyJoinColumn
private Konto konto
...
}
@Entity
public class Konto {
@Id @GeneratedValue(generator = "myForeignGenerator")
@org.hibernate.annotations.GenericGenerator(
name = "myForeignGenerator",
strategy = "foreign",
parameters = @Parameter(name = "property", value = "owner")
)
private long id;
private Person owner;
...
}
One-To-One
Unique Foreign Key
Person
Konto
id : long
vorname : String
nachname : String
nummer : String
blz : String
PERSON
ID NUMBER <PK>
VORNAME VARCHAR
NACHNAME VARCHAR
KONTO_ID NUMBER <FK><UNIQUE>
KONTO
ID NUMBER <PK>
NUMMER NUMBER
BLZ VARCHAR
One-To-One
Unique Foreign Key
@Entity
public class Person {
@Id
private long id;
private String vorname;
private String nachname;
@OneToOne
@JoinColumn(name=“KONTO_ID“, nullable=false)
private Konto konto
...
}
@Entity
public class Konto {
@Id
private long id;
private Integer nummer;
private String blz;
...
}
Bidirektionale Relationen
•
JPA verwaltet bi-direktionale Relationen nicht
von selbst
•
•
Im Code muss Konsistenz garantiert werden!
•
•
Sonst: doppeltes Fremdschlüssel Mapping
Ein Ende ist invers, d.h. JPA ignoriert diesen Teil
bei Verwaltung und Mapping verweist auf das
nicht-inverse Ende
Tip: Bidirektionale Mappings nach Möglichkeit
vermeiden
Many-To-One Bidirektional
Person
Konto
id : long
vorname : String
nachname : String
nummer : String
blz : String
PERSON
ID NUMBER <PK>
VORNAME VARCHAR
NACHNAME VARCHAR
KONTO_ID NUMBER <FK>
KONTO
ID NUMBER <PK>
NUMMER NUMBER
BLZ VARCHAR
Many-To-One / One-To-Many
Bidirektional
@Entity
public class Person {
@Id
private long id;
private String vorname;
private String nachname;
@ManyToOne
@JoinColumn(name=“PERS_KTO_ID“, nullable=false)
private Konto konto
...
}
@Entity
public class Konto {
@Id
private long id;
private Integer nummer;
private String blz;
@OneToMany(mappedBy=“konto“)
private Set<Person> eigentuemer = new HashSet<Person>();
...
}
Verwaltung von Bidirektionalen
Relationen
@Entity
public class Konto {
@Id
private long id;
private Integer nummer;
private String blz;
@OneToMany(mappedBy=“konto“)
private Set<Person> eigentuemer = new HashSet<Person>();
public void addEigentuemer(Person p) {
if (p == null) {
throw new IllegalArgumentException(“p is null“);
}
if(p.getKonto() != null) {
p.getKonto().getEigentuemer().remove(p);
}
getEigentuemer().add(p);
p.setKonto(this);
}
...
}
Many-To-Many
Person
id : long
vorname : String
nachname : String
PERSON
Team
name : String
PERS_TEAM
TEAM
ID NUMBER <PK>
PERS_ID NUMBER <FK>
ID NUMBER <PK>
VORNAME VARCHAR
TEAM_ID NUMBER <FK>
NAME VARCHAR
NACHNAME VARCHAR
Many-To-Many
@Entity
public class Person {
@Id
private long id;
...
@ManyToMany
@JoinTable(name=“PERS_TEAM“,
joinColumns = @JoinColumn(name=“PERS_ID“),
inverseJoinColumns = @JoinColumn(name=“TEAM_ID“)
)
private Set<Team> teams = new HashSet<Team>();
...
}
@Entity
public class Team {
@Id
private long id;
private String name;
@ManyToMany(mappedBy=“teams“)
private Set<Person> personen = new HashSet<Person>();
...
}
Lebenszyklus und
transitive Persistenz
• Entitäten haben einen eigenständigen
Lebenszyklus
• TransientObjectException bei folgendem
Code ohne transitive Persistenz
Konto k = new Konto();
Person p = new Person();
...
k.setPerson(p);
session.save(k);
Cascade konfiguriert
transitive Persistenz
•
JPA unterstützt folgende Cascades
•
•
•
•
•
•
ALL
PERSIST
MERGE
REMOVE
REFRESH
DETACH
Hibernate hat
andere
Semantik!
Cascading
@Entity
public class Person {
@Id
private long id;
private String vorname;
private String nachname;
@ManyToOne(cascade=CascadeType.ALL)
private Konto konto
...
}
Sonderfall: Orphan
Deletion
•
•
Nur für Collections
•
Zwei Konfigurations Möglichkeiten:
Löscht eine Entität wenn sie aus einer Collection
entfernt wird
•
•
@OneToMany / ManyToMany (orphanRemoval=true)
@org.hibernate.annotations.Cascade(
CascadeType.DELETE_ORPHAN)
Agenda
Zeit
Thema
08:00 - 09:00
Einleitung und theoretische Grundlagen
09:00 - 09:30
JPA im Überblick
09:45 - 10:30
Mapping von Klassen
10:30 - 11:15
Arbeiten mit Hibernate
11:15 - 11:45
Queries
Arbeiten mit JPA und
Hibernate
• Lebenszyklus von Entitäten
• EntityManager / Session API
Lebenszyklus von
Entitäten
new()
Transient
gc
save() / persist()
saveOrUpdate()
merge()
Session
.get()
.load()
.getReference()
.find()
Query
.list()
.uniqueResult()
.iterate()
.scroll()
.getResultList()
.getSingleResult()
Persistent
evict()
clear()
close()
Detached
delete()
remove()
Removed
update()
saveOrUpdate()
merge()
gc
gc
Lebenszyklus von
Entitäten
Zustand
Von Hibernate
„Verwaltet“
Datenbank ID
transient
nein
nein
persistent
ja
ja
detached
nein
ja
Speichern
• Ein Objekt wird „persistent“
• session.save() gibt die generierte ID sofort
zurück (return Serializable)
• em.persist() gibt keine ID zurück (return
void)
Löschen
• em.remove() und session.delete() löschen
eine Entity und setzen den Status „removed“
• remove() ist JPA API
• Löschen grosser Datenbestände über HQL
• hibernate.use_identifier_rollback = true
setzt den ID Wert zurück (Status =
„transient“)
Laden
• get() und load() laden persistente Entities
• find() und getReference() sind JPA API
existiert
existiert nicht
Zugriff
get()
Entity
null
Entity / null
find()
Entity
null
Entity / null
load()
Proxy
Proxy
Entity / Exception
getReference()
Proxy
Proxy
Entity / Exception
Updates
• Default: alle Spalten sind im Update enthalten
• dynamic-update=“true“ bewirkt dynamische
SQL Generierung
• Eigene Dirty Checking Implementierung kann
in Interceptor (findDirty())implementiert
werden
Reattaching
• session.update() löst immer SQL update aus
• select-before-update konfiguriert dirty check
mittels SELECT (ineffizient, aber bei „on
update“ Triggern interesssant)
• lock() löst kein update aus, sondern führt nur
Reattach durch
Reattaching
• merge() verhindert
NonUniqueObjectException
• Das übergebene Objekt ist nicht attached,
sondern das zurückgegebene
• Kopiert alle Properties und Collections
• Transiente Objekte werden gespeichert
Reattaching
merge
saveOrUpdate
Objekt mit gleicher ID
bereits in Session
Objekt in Session wird
überschreiben
NonUniqueObjectException
Objekt ist Transient
INSERT
INSERT
Zustand übergebenes Objekt
Transient / Detached
Persistent
Zustand Rückgabe Objekt
Persistent
(returns void)
Bestandteil von JPA
ja
nein
Agenda
Zeit
Thema
08:00 - 09:00
Einleitung und theoretische Grundlagen
09:00 - 09:30
JPA im Überblick
09:45 - 10:30
Mapping von Klassen
10:30 - 11:15
Arbeiten mit Hibernate
11:15 - 11:45
Queries
Arbeiten mit Queries
Query q = em.createQuery(“from Person p where p.nachname = :nachname “)
.setParameter(“nachname“, “Müller“)
.setFirstResult(20)
.setMaxResults(10);
List ergebnisse = q.getResultList();
Query q = em.createQuery(“from Person p where p.username = :login “)
.setString(“login“, “meinlogin“)
Person p = (Person)q.getSingleResult();
Criteria c = session.createCriteria(Person.class);
c.setFirstResult(20);
c.setMaxResults(19);
List ergebnisse = q.list();
Named Queries
@NamedQueries({
@NamedQuery(
name=“findPersonByUsername“,
query=“from Person p where p.username = :login“
)
})
@Entity
public class Person {
@Id @GeneratedValue
@Column(name=“PERS_ID“)
private long id;
@Column(name=“PERS_USERNAME“)
private String username;
....
}
Query q = em.createNamedQuery(“findPersonByUsername“)
.setParameter(“login“, username);
List ergebnisse = q.getResultList();
JPA-QL
• Textuelle Abfrage Sprache
• Ähnlich zu SQL
• Geht gegen Objekte und nicht gegen
Tabellen
• Voll Objektorientiert
• Starkte Verwandschaft zu HQL
• Query Objekt wird über EntityManager
bezogen
Einfache JPQL Beispiele
from Person
from Person as p
select p from Person as p
Diese Abfragen
machen alle das
selbe
select p from Person p
SQL: SELECT * FROM PERSONEN
JPQL ist polymorph
from Zahlungsart
from LastschriftEinzug
Zahlungsart
Lastschrift
from Kreditkarte
Kreditkarte
from java.lang.Object
from java.io.Serializable
Einschränkungen mit
where
from Person where username = :login
from Person p where p.adresse.strasse = :strasse
from Person p where
p.email like ‘%@senacor.%‘
and p.adresse.stadt = ‘Nürnberg‘
and p.lastLogin < :vorgestern
Sortierung mit
order by
from Person p order by p.lastname asc
from Person p order by p.lastname asc,
p.firstname asc
Selektion
select p.lastname, p.firstname from Person p
• Gibt Object Array zurück
• Rückgabewerte sind keine Entitäten!
Aggregation und
Gruppierung
select p.adresse.stadt, count(p)
from Person p
group by p.adresse.stadt
select p.adresse.stadt, count(p)
from Person p
group by p.adresse.stadt
having p.adresse.stadt like ‘Bad %‘
Inner und Outer Join
PERS_ID
PERS_NAME
1
...
KTO_id...
KTO_NR
Michael
1
1234
1
Michael
2
5678
2
Tina
3
645234
2
Tina
4
98798
2
Tina
5
76532
PERS_ID
PERS_NAME
KTO_id...
KTO_NR
1
Michael
1
1234
1
Michael
2
5678
2
Tina
3
645234
2
Tina
4
98798
2
Tina
5
76532
3
Sepp
null
null
4
Melanie
null
null
...
SELECT p.*, k.*
FROM Person p
INNER JOIN Konto k
on p.ID = k.PERS_ID
SELECT p.*, k.*
FROM Person p
LEFT OUTER JOIN Konto k
on p.ID = k.PERS_ID
Joins in JPQL
• Implizite Navigation
from Person p where p.adresse.stadt = ‘ Regensburg‘
• Inner Join
from Person p join p.konten k
where k.balance > 0
• Fetch Join
from Person p left join fetch p.konten
QueryDSL
Einführung
• Typ-sicher durch Annotation Processor
• Unterstützung für SQL, Hibernate / JPA und
MongoDB, JDO, Lucene
• Volle Code Completion
• Fluent-API
Maven
<project>
<build>
<plugins>
...
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.0.6</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
</build>
</project>
Formulierung von
Abfragen
HibernateQuery query = new
HibernateQuery(session);
JPAQuery query = new JPAQuery(entityManager);
Einschränkungen mit
Restrictions
QPerson person = QPerson.person;
Person p = query.from(person)
.where(person.username.eq(“ploed“))
.uniqueResult(person);
QPerson person = QPerson.person;
List<Person> p = query.from(person)
.where(person.alter.between(“10“, “14“),
person.lastname.like(“P%“))
.list(person);
Sortierung mit
orderBy
QPerson person = QPerson.person;
List<Person> p = query.from(person)
.where(person.alter.between(“10“, “14“),
person.lastname.like(“P%“))
.orderBy(person.lastname.asc())
.list(person);
Gruppierung mit
Projections
QPerson person = QPerson.person;
JPAQuery query = new JPAQuery(entityManager);
List<Object[]> result = query.from(person)
.groupBy(person.adresse.stadt)
.listDistinct(person.adresse.stadt,
person.adresse.stadt.count();
Joins
QPerson person = QPerson.person;
JPAQuery query = new JPAQuery(entityManager);
List<Person> l = query.from(person)
.innerJoin(person.fotos)
.orderBy(person.username.asc())
.distinct().list(person);
Herunterladen