Professional Documents
Culture Documents
class SINGLE_TABLE
Parent SINGLE_TABLE
ChildA ChildB
Alexander Kunkel
Inhaltsschwerpunkte
Arbeitsumgebung
Java-Annotations
Java Persistence API Einführung
Entity
Beziehungen
Vererbung
Datenabfragen
Sortieren
Caching
Sperrstrategien
Designempfehlungen
• Ab JPA 2.0
2.0
Starten der
HSQL
Datenbank.
Tabellen und
ihre Klassen
Diverse Testprogramme
Datenbank-Verbindung
Datenbank-dialekt
DB-Schema
automatisch erzeugen
Annotierte Klassen
automatisch finden
• MyClass MyClass
●
Testklasse mit annotierten public class MyClass {
String s1;
Stringfeldern.
@Length(max = 32)
String s2;
@Length(max = 15)
String s3;
tst.annotation.length
• 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) {
}
}
[...]
tst.annotation
• TestLength TestLength
– Das passende Testprogramm dazu. public class TestLength {
System.out.println(mc);
}
}
Konsole
Field 's3' is too long.
tst.annotation.length
• 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
tst.firstentity
• .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.
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;
tst.firstentity
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;
tst.firstentity
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
tst.firstentity
load() rollback()
Query.list() commit()
close()
clear()
persist()
new merge()
Transient Persistent
remove()
Removed persist() persist()
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.
tst.primarykey
tst.primarykey
Einfacher Generator
H!
Person
Testcode DB
tst.idgenerator
• @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 ...
String lebenslauf;
• Benützen von BLOBS und CLOBS
erfolgt unabhängig von der @Lob
public String getLebenslauf() {
Datenbank. return lebenslauf;
• Für Java-Typen: }
tst.blobs
• 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
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
ORACLE DDL-Skript:
Längenbeschränkung
Unique
tst.constraints
Foreign Key Siehe „Beziehungen“
Alexander Kunkel 20.10.08 37
Hibernate Übersicht
Validatoren H!
Beispielvalidierungen
• Im Beispiel werden die Klassen Person und - nam e: String - strasse: String
Telefon
PERSON TELEFON
«colum n» «colum n»
ID ID
NAM E NUM M ER
ST RASSE PERSON_FK
class Embedded
• Anhand der 1:1 Beziehung lassen sich schon die
meisten Konstrukte demonstrieren. -
Person
nam e: String -
Adresse
strasse: String
tst.cascade
Person
class Embedded
private Em_Adresse adresse;
Person Adresse
«co lum n»
ID
Adresse
NAM E
ST RASSE
@Embeddable
public class Em_Adresse {
...
person.setAdresse(adresse);
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
tst.one2one.embedded
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") })
@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
Testcode
EntityManager em = HibernateUtil.getEntityManager();
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());
em.getTransaction().begin();
person.setAdresse(null);
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
em.remove(person.getAdresse());
person.setAdresse(null);
Hibernate-Log
select fk_person0_.id as id2_1_, ...
update Fk_Person set adresse_fk=?, …
delete from Fk_Adresse where id=?
tst.one2one.foreignkey
«colum n» «colum n»
ID ID
NAM E NUM M ER
PERSON_FK
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
«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
tst.one2many
tst.one2many
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
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
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
person = em.merge(person);
List<O2m_Telefon>
telefonNummern = person.getTelefonNummern();
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=?
Person
Abändern der Strategie
bei Attributen von
EAGER auf LAZY
tst.lazyeager
Person Adresse
private Bi_Adresse adresse; private Bi_Person person;
tst.bidir
Person Telefon
private List<Bi_Telefon> telefonNummern; private Bi_Person person;
Person Testcode
tst.one2one.foreignkeyreverse
Person Telefon
private List<CA_Telefon> telefonNummern; private CA_Person person;
Testcode Hibernate-Log
EntityManager em = HibernateUtil.getEntityManager(); select … from CA_Person … where …
Tabellen abgebildet.
bewerkstelligt.
DISCRIM INAT OR
AT T RIBUT E_P
• Vorteile
– Klassen werden eins zu eins ChildA ChildB CHILD_A CHILD_B
class TABLE_PER_CLASS
optional.
• Vorteile ChildA ChildB
.find(…)
Hibernate-Log
### Anhand Primärschlüssel vollständig laden
select q_person0_.id as id11_1_, q_person0_.name as name11_1_, …
.getReference(…)
tst.query
query = em.createQuery(...);
query = em.createNativeQuery(...);
query = em.createNamedQuery(...);
query.setMaxResults(count);
FlushModeType.AUTO (default)
FlushModeType.COMMIT
H!
Achtung!
Nur in Ausnahmefällen verwenden.
Momentan ist jeder Hinweis
produktabhängig.
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.
Einfacher Select
Fetch Join
Hibernate-Log
### Einfacher Select als FETCH JOIN
select q_person0_.id as id11_0_, telefonnum1_.nummer as nummer12_1_, …
tst.query
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
Syntax:
QL_statement ::= select_statement | update_statement | delete_statement
QL_statement
Syntax:
select_statement ::= select_clause from_clause [where_clause]
[groupby_clause] [having_clause] [orderby_clause]
select_statement
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
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
Hibernate-Log
select q_person0_.name ... from Q_Person q_person0_
###[0] Duck
###[1] Donald
###[0] Clever
###[1] Class
tst.query.Q_select_clause
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();
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
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();
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
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
Syntax:
from_clause ::= FROM identification_variable_declaration
{, {identification_variable_declaration | collection_member_declaration}}*
geschachtelt sein.
• Verschiedene Join-Typen:
●
Inner Join
●
Outer Join
●
Fetch Join
●
Impliziter Join bei n:1 und 1:1 in der
select_clause
Syntax:
identification_variable_declaration ::= range_variable_declaration { join |
fetch_join }*
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
Syntax:
identification_variable_declaration ::= range_variable_declaration { join | fetch_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“.
Syntax:
identification_variable_declaration ::= range_variable_declaration { join | fetch_join }*
• 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
tst.fetch
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
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
@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'
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
@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 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
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.
tst.oracle.NullOrEmpty
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
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
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=?
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
tst.locking