Dienstag, 20. März 2012

Persistieren mit JPA: persist(), merge(), remove(), clear(), close() oder doch flush()?

Welche JPA - Methode sollte in welcher Situation genutzte werden?
JPA bietet einige Methoden an, um mit dem PersistenzManager zu arbeiten:
  • persists()
  • merge()
  • remove()
  • flush()
  • close()
Was passiert beim persist() / flush() / close?
Beim persist() wird die transiente Entität in den Persistenkontext aufgenommen, d.h. die Entität hat eine Verbindung zum EntityManager erhalten. Diese Entität erhält aber nicht sofort eine Repräsentation in der Datenbank!
Wird beispielsweise die Transaktion zurückgerollt, aufgrund eine SQL-Fehlers oder mittels eines expliziten
rollbacks, dann wird die transiente Entität oder Änderungen an anderen Entitäten nicht in der Datenbank abgespeichert:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistence");
EntityManager em = emf.createEntityManager();

...
em.getTransaction().rollback();
...

Möchte man nun innerhalb einer Transaktion an bestimmten Stellen die Synchronisation mit der Datenbank auslösen, dient flush().
Was passiert bei detachten Entitäten beim flush-Aufruf? Nichts.
Was passiert mit persistent Entities, die mit transienten Entities verbunden sind? IllegalStateException
Was passiert mit einer persistent Entity, die mit einer detached Entity verbunden ist? Die Beziehung zur detached Entity wird persistiert, wenn die persistent Entity Besitzer dieser Beziehung ist.

Überlicherweise wird bei Beendigung der Transaktion die gemachten Änderungen an den Entitäten und die neu angelegten persistenten Entitäten in der Datenbank vom JPA-Provider gespeichert.

Bei einem close() wird der EntityManager geschlossen und wie bei clear() werden alle Entitäten vom EntiyManager gelöst.
Diese Entitäten gehen in den Zustand detached über und werden so nicht mehr mit der Datenbank synchronisiert. Um genau diese wieder zu ermöglichen gibt es die Methode merge().
Hiermit können detached-Entitäten wieder in den Persistenzkontext überführt werden.
Dabei werden die Attributwerte der zu mergenden Entität in die bestehende persistente Entität hineinkopiert.
Fall zur detached Entität in der Session keine persistente Entität befindet, dann wird
versucht die betreffende Entität zu laden oder sie wird angelegt.

Eine Entität wird mit remove() aus dem Persistenzkontext genommen und bei Transaktionsende aus der Datenbank gelöscht. Wird auf eine gelöscht markierte Entität merge() aufgerufen, so wird eine IllegalArgumentException geworfen.






Sonntag, 18. März 2012

Hibernate 4.1 neue Features laden per @NaturalId

Welche Features kommen im neuen Hibernate 4 Release?
Im 4. Release, bzw. genau genommen im 4.1-Release, kann man in Hibernate Entitäten laden mittels
naturalIds.

Was sind naturalIds?
NaturalIds sind Schlüssel mit fachlicher Eindeutigkeit, die in diversen Abfragen / Ladensituationen genutzt werden. Es wird angeraten immer technische Schlüssel zu verwenden, wenn diese Entität
über Attribute mit fachlicher Eindeutigkeit besitzt, können diese mit der hibernate-Annotation
@NaturalId versehen werden. Dazu muss die Property unique sein, da
eine unique-Constraint in der Datenbank auf dieser Spalte angelegt wird.


Wozu der Aufwand @NaturalId - Annotationen zu vergeben?
Angenommen es gibt eine Entität Steuerzahler, dieser hat sicherlich eine property namens
Steuernummer. Die Steuernummer sollte eindeutig sein.
Somit ist die property ein Anwärter für die Definition eines naturalId -Schlüssel.

public class Taxpayer {
  @Id
  @GeneratedValue
   private Long id;
   private String firstname;
   ....
   @NaturalId
   private String taxnumber;
   ...
}

Nun ergibt sich der Vorteil bereits in der älteren Hibernate-Versionen mittels Criteria-API das Vorhandensein
eines @NaturalIds in Abfragen zu nutzen:
Session s = ...
Criteria crit = s.createCritera(Taxpayer.class);
Taxpayer t = (Taxpayer) crit.add(Restrictions.naturalId("taxnumber").set("89089083AST8908").uniqueResult();

Neues 4.1 feature:
Mit 4.1 läßt sich ein definiertes @NaturalId in den Entität auf session-Ebene verwenden, d.h. eine Entität kann mittels byNaturalId() geladen werden!
Session s = ...
TayPayer t = (TaxPayer) s.bySessionId(Taxpayer.class).using("taxnumber", "89089083AST8908");


Gibt es Nachteile?
Leider ist @NaturalId ein hibernate specifisches feature.
Man kann leider keine @NaturalIds per JPA definieren.
Es hat auch den Anschein, dass @NaturalId nicht so bald in den kommenden JPA2.1 Standard aufgenommen wird: siehe Early Draft JPA 2.1


Samstag, 17. März 2012

JPA find() vs. getReference()

Wie werden Entitäten im Zustand persistent in JPA geladen/geholt?
Hierzu gibt es 2 Möglichkeiten neben diversen query-Methoden:

  • find()
  • getReference()
Wie werden beide genutzt?
- Find() wird auf dem EntityManager aufgerufen, die Methode erwartet 2 Parameter:
  1. Typ der angeforderten Entität
  2. Identität: Id der gefordeten Entität
Zurückgeliefert wird die gefundene Entität ohne null.
Beispiel:
EntityManagerFactory ef = Persistence.createEntityManagerFactory("myapp") ;
EntityManager em =  ef.createEntityManager();
....
Kunde k = em.find(Kunde.class, 70992);


- GetReference() wird ähnlich genutzt:
Kunde k = em.getReference(Kunde.class, 70922);
Falls die Entität mit der angegebenen Id nicht im Persistenzkontext vorliegt, wird eine EntityNotFoundException() geworfen.

Wodrin besteht der genaue Unterschied?
Bei find() wird die Entität aus dem Cache des Persistence-Context zurückgegeben oder wenn er nicht vorliegt aus der Datenbank geladen und zurückgegeben.
Bei getReference() wird die Entität erst einmal noch nicht geladen, sondern es wird ein Proxy(ein spezielles Objekt, so genanntes Stellvertreterobjekt mit angereicherten Methoden zum Nachladen der eigentlichen Entität) zurückgegeben - Realisierung über LazyLoading. Erst wenn auf konkrete Attribute zugegriffen wird oder andere Persistenzmethoden aufgerufen werden, greift der Proxy ein und lädt die konkrete Entität nach.

Wann sollte man welche Methode benutzen?
Die Nutzung von find() sollte nach Möglichkeit immer Vorrang vor query-Methode haben, da
find() geladene Entitäten aus dem Cache des Persistenz-Kontext zurückgeben kann.
Wenn man weiß, dass die Entität erst später konkret verwendet wird, ist die Nutzung von
getReference() anzuraten.

JPA Lebenszyklus Transient Persistent Detached

Wie sieht ein Lebenszyklus einer JPA entity aus?
Wenn eine Entität definiert wurde, später instaniiert wird, dann beginnt der Lebenszyklus einer
Entität. Der Lebenszyklus eine Entität endet mit einem remove() bzw. einem detach(siehe weiter unten).
Eine Entität kennt nicht den Zustand seines Lebenszyklus, d.h. sie kann hierzu nicht abgefragt werden.

Welche Stadien durchläuft eine JPA in ihrem Leben?
  • transient
Wurde eine Klasse mit @Entity deklariert, dann handelt es sich bei dieser Klasse um eine Entität, d.h. sie wir im Persistenzkontext verwendet und wird vom EntityManager verwaltet.
Wird diese Klasse instaniiert, dann befindet sie sich im transienten Zustand, d.h. es ist ein einfaches Java-Objekt, dass nicht mit der Datenbank synchronisiert wird.

  • persistent
Wird auf dieser Entität nun mittels des EntityManager persist() - aufgerufen, dann wird die Entität in den Persistenzkontext aufgenommen, d.h. die Entität wurde mit einem/dem EntityManager verbunden.
Die Entität wird nun bei jedem Transaktionsende (commit() oder flush()) mit der Datenbank synchronisiert.

  • detached
Wird eine Entität explizit aus dem Persistenzkontext genommen, durch detach() oder der EntityManager geschlossen via close(), dann wird die Verbindung(siehe persistent()) wieder aufgelöst. D.h. die Entität wird nun nicht mehr mit der Datenbank synchronisiert, wenn z.B. die Transaktionen zu Ende ist.

Eine detached - Entität kann mit merge()-Aufruf wieder in den Persistenzkontext aufgenommen werden.
Eine persistente Entität kann mittels remove()-Aufruf in den Zustand transient überführt werden, die Daten der Entität werden in der Datenbank gelöscht.

Mittwoch, 14. März 2012

JPA2 Feature Speichern der Reihenfolge von Elementen einer Assoziation

Es gibt nun mit JPA 2 ein neues Feature mit dem man bei einer Assoziation die Reihenfolge der Elemente
sicherstellen kann, d.h. die Reihenfolge der Elemente einer Assoziation, die als Liste geführt wird,
wird in der Datenkbank mitgespeichert.
Gegeben sei eine 1:n - Assoziation.
Die 1-Seite hat die betreffende Collection mit Entitäten der n-Seite.
Diese Collection kann als List definiert sein in der Klasse der Entität.
Möchte man die Reihenfolge der Liste aber immer deterministisch haben, dann muss
die Reihenfolge mitgespeichert werden. Hierzu gibt es eine neue Annotation in JPA 2:
@OrderColumn

@Entity
public class A implements Serializable {
   ...
   @OneToMany
   @OrderColumn
   private List<B> bs = new ArrayList<B>();
   ...
}    

@Entity
public class B implements Serializable {
 ...
 @Id
 @Generated
  private Long id;
  private String orderNumber;
 ...
}

In diesem Fall wird in der Entität B ein von der Entität nicht sichtbares Feld angelegt mit dem Namen
BS_ORDER, welches dazu dient, die Reihenfolge der assozierten Entitäten von B zu A festzuhalten.
Somit ergibt sich immer wieder die gleiche Reihenfolge Elemente in der Collection, wenn die Entitäten von A geladen werden z.B. im Modus FetchType.EAGER.

Wann anwenden?
Wenn eine Sortiervorschrift @OrderBy die Zugriffszeit unnötig verschlechtert oder die Sortierung nach fachlichen Attributen / Spalten immer noch eine undefinierte Sortierung hervorbringt.

Das Speichern von Elementen per Key bei einer Assoziation, die als Map realisiert wurde,
war auch mit der vorherigen JPA Version möglich:
Im obrigen Beispiel ist bs nicht als List angegen worden, sondern als Map.
Dann kann mit der Annotation @MapKey der Schlüssel festgelegt werden, mit der man auf
die Collection / Assoziation zugreifen möchte.
Im Default bezeichnet @MapKey immer den primary key der Entität.
Möchte man eine andere Spalte als Schlüssel für die Map definieren, kann man dies mit
@MapKey(name="columnName") festlegen.
So ergibt sich nach obigen Beispiel:
   @OneToMany
   @MapKey(name="orderNumber")
   private Map<String, B> bs = new HashMap<String,B>();

Wozu der keybasierte Zugriff auf Assoziatioen?
Der macht Sinn, wenn man weiß, dass bei dieser Annotation der Zugriff
aus der Fachlogik meistens mit der orderNumber erfolgt und der pk ein technischer Schlüssel ist,
der in anderen Use Cases wie Beispiel Archivierung eher zum Zuge kommt.
Dann lässt sich schön ausdrücken:
String aOrderNumber;
....
B myBEntity = a.getBs().get(aOrderNumber);


Dienstag, 13. März 2012

Behandlung von Value Types in JPA Hibernate

Wie wird mit dem Thema value types in JPA / Hibernate umgegangen?
Zunächst einmal die Definition von value types.
Value types sind keine echten Entitäten. Sie haben keinen Lebenszyklus, dass bedeutet sie werden nicht angelegt, editiert oder gelöscht. Sie gibt es seit Anbeginn des Systems.
Echte Entitäten, also mit @Entity gekennzeichnet, haben sehr wohl einen Lebenszyklus und haben eine entsprechende Repräsentation in der Datenbank.
Nun gibt es diverse Fälle wie man value types nutzen möchte:
  • als Konstanten, die bei dem entsprechenden Attribut der Entität in der Datenbank gespeichert wird
  • Werte, die lediglich als solche zu einer betreffenden Entität gespeichert werden sollen
Konstanten können schön mit Java Enums definiert werden:
public enum EnumXY{
   constantValue1, constantValue2, constantValue3;
}

Verwendet werden können die Konstanten in jeder Entität:
@Entity
public class MyEntity implements Serializable {
    ...
    @Enumerated
    private EnumXY kennzeichen;
    ...
}

In der Tabelle wird nun im Default die Position des Wertes(Ordinalzahl) der betreffenden Entität als Integer - Wert gespeichert. Würde also MyEntity das Kennzeichen constantValue3 zugewiesen bekommen, dann stünde der Wert 2 in der Tabelle von MyEntity. Dies kann über @Enumerated(EnumType.ORDINAL) (default) oder @Enumerated(EnumType.STRING) gesteuert werden. Beim letzteren wird der Name der Konstante in der betreffenden Spalte in der Datenbank gespeichert. Hier also constantValue3.

Was ist mit den Fällen, in denen man mehrere Werte eines value types zu einer Entität speichern möchte?
Auch dies ist nun mit JPA 2 möglich:
Mittels @ElementCollection wird deklarativ bestimmt, dass die Collection aus Werten eines value types besteht und sich nicht wie gewöhlich um eine Collection bestehend aus Entitäten handelt.
@ElementCollection
private List<String> basetypeValues = new ArrayList<String>();
Erst einmal CollectionsOfElements ist die Hibernate spezifische Annotation.
In JPA gibt es hierfür @ElementCollection.
Beispiel:
@Entity
public class X implements Serializable {
  @Id
  @Generated
  private Long id;
  @ElementCollection
   private List<String> remarks = new ArrayList<String>();
    ...
}

@Test
public void testElementCollection() {
  ...
  X x = new X();
  private List<String> remarks = new ArrayList<String>();
  remarks.add("remark1");
  remarks.add("remark2");
  remarks.add("remark3");
  x.setRemarks(remarks);
  em.persist(x);
  ...
}

Was passiert in der Datenbank?
Eine "ElementCollection" wird immer in einer separaten Tabelle gespeichert.
Diese kann man noch genauer über @CollectionTable definieren, dabei dient joinColum(s) zur Definition des foreignKey(s):
@CollectionTable(
   name = "nameOfCollectionTable",
   joinColumns = @JoinColum(name="fk")
)

Resultat:

Table X:
id column1 column2
3 .....         ....

Table Remarks:
id remarks 
3  remark1
3  remark2
3  remark3








Montag, 12. März 2012

JPA Vererbung SINGLE_TABLE JOINED TABLE_PER_CLASS

Der Nutzen des Einsatzes eines O/R-Mappers wie z.B. Hibernate unter Verwendnung der JPA-Spezifikation ist ja, dass db-unabhängig entwickelt werden kann und man den Kontext der objektorientieren Welt für Persistenzthemen nicht verlassen muss.
Somit möchte man natürlich Vererbung auch per JPA ausdrücken können.
Hierzu gibt es diverse Vererbungsstrategien:
Default ist SINGLE_TABLE, wenn lediglich die Annotation @Inheritance angegeben wurde.
Es gibt daneben noch 2 andere:
@Inheritance(strategy=InheritanceType.JOINED) und
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS).

Was bedeuten diese Strategien?
Beim Standard(SINGLE_TABLE) werden alle Unterklassen der Vererbungshierarchie auf eine Datenbank-Tabelle gemapped.
Bei JOINED werden alle abstrakten Klassen und konkreten Klassen der Vererbungshierarchie in einer separaten Tabelle persistiert.
Bei TABLE_PER_CLASS wird für jede konkrete Klasse der Vererbungshierarchie eine eigene dedizierte Tabelle angelegt.

Welche Strategie sollte man situationsbedingt auswählen?
Die Antwort auf diese Frage ergibt sich aus vornehmlich 2 Aspekten:
  • Polymorphie
  • Performance
Jeder möchte hohe Performance - es gibt jedoch Situationen, in denen Flexibilität und Erweiterbarkeit noch vor der Performance eingestuft wird: bspw. neue Features, die Konfigurationen oder untergeordnete UseCases betreffen.
Es gibt Situationen, in der die Vererbungshierarchie wichtige Bewegungsdaten betrifft und im Kern von wichtigen UseCases steht - in dem Fall ist Performance wichtiger als Flexibilität.
SINGLE_TABLE hat den Vorteil des perfomanten Zugriffs, da alles in einer Tabelle gespeichert wird.
Unterscheiden sich die konkreten Unterklassen stark und ist die Anzahl hoch, ergibt sich eine sehr breite Tabelle, so dass sich wiederum ein ungünstiger Tablespace-Bereich ergibt und weniger von der Tabelle von der Datenbank gecacht werden kann. In diesem Fall ist SINGLE_TABLE eine schlechte Wahl.
Nachteil Datenintegrität:
Bei SINGLE_TABLE müssen alle nicht PK-Felder zudem nullable sein, da alle Unterklassen in dieser Tabelle gespeichert werden und einige Typen diverse Spalten nicht verwenden.
Weiß man, dass man in den meisten und wichtigsten Zugriffsfällen konkrete Typen aus dem Persistenzkontext haben möchte, bietet sich TABLE_PER_CLASS an. Bei dieser Strategie sind polymorphe Abfragen sehr unperformant aufgrund von resultierenden UNION-Abfragen und Nutzung polymorpher Beziehung ist nicht möglich, da die abstrakten Typen nicht in der Datenbanktabelle gespeichert sind.
Bei JOINED sind polymorphe Abfragen aufgrund vorliegendem @DescriminatorValue in der Tabelle möglich - realisiert durch outer joins (besser als UNIONS). Konkrete Typenabfrage werden über inner joins realisert - daher der Name.

Realisierung
Es gibt eine abstrakte Klasse. Die Subklassen erben davon und sind @Entity.
Bei SINGLE_TABLE und JOINED muss nach JPA-Standard eine @DiscriminatorValue angegeben werden. Dieser ist vom Typ String und trägt im Default den Namen dtype und dient dazu die konkreten Klassen voneinander zu unterscheiden: In der Datenbank wird in der Tabelle der jeweilige String in die Spalte dtype geschrieben.

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class BaseClass implements Serializable {
@Id
@GeneratedValue
private Long id;
private String common;
 ...
}

@Entity
@DiscriminatorValue(value="A")
public class A extends BaseClass {
 private String specificA;
...
}
@Entity
@DiscriminatorValue(value="B")

public class B extends BaseClass {
 private String specificB;
 ...
}
Ergibt folgende Tabelle:
dtype|id|common|specificA|specificB

Bei einer Instanz A wird die Spalte specificB immer null sein.
Auf der anderen Seite sind polymorphe Abfragen (select * from BaseClass) oder nach konkrete Subklasse (select * from A ...) hochperformant.

Wäre es TABLE_PER_CLASS sind polymorphe Abfragen nicht möglich.
Bei JOINED werden bei Abfragen nach konkreten Subklassen inner joins generiert,
bei polymorphen Abfragen outer joins.