Java Persistence API 2: Unterschied zwischen den Versionen

Aus Alexander's Wiki
 
(21 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
[[Kategorie:Java]]
JPA ist eine Spezifikation, die von einem JPA-Provider implementiert werden muss. Die bekanntesten sind:
JPA ist eine Spezifikation, die von einem JPA-Provider implementiert werden muss. Die bekanntesten sind:


Zeile 6: Zeile 7:


== Entitäten ==
== Entitäten ==
Die Annotation '''@Entity''' macht aus der gewöhnlichen, nicht persistenten Java-Klasse eine persistente Entity. Die Instanzvariable id ist mit '''@Id''' annotiert und wird damit von JPA als Primärschlüsselvariable verwendet.
Die Annotation '''@Entity''' macht aus der gewöhnlichen, nicht persistenten Java-Klasse eine persistente Entity. Mit ''implements Serializable'' wird das Objekt serialisierbar, was z.B. für die Verwendung bei [http://de.wikipedia.org/wiki/Remote_Method_Invocation RMI] benötigt wird.
 
Die Instanzvariable id ist mit '''@Id''' annotiert und wird damit von JPA als Primärschlüsselvariable verwendet. Ohne weitere Annotation muss die Anwendung dafür Sorge tragen, dass die Entity vor dem Persistieren mit einem gültigen, also noch nicht vergebenen Primärschlüssel versorgt wird.
 
<source lang="java5">
import javax.persistence.*;
 
@Entity
public class DemoKlasse {
 
  @Id
  private String id;
  private String param;
 
  public DemoKlasse() {
    // Hibernate und OpenJPA bieten eigene Generatoren an
    this.id = UUID.randomUUID().toString();
  }
 
  public DemoKlasse(String param) {
    this();
    this.param = param;
  }
 
}
</source>
 
Die Generierung des Primärschlüssel kann auch automatisch erfolgen. Dazu wird die Annotation '''@GeneratedValue''' gefolgt von einer der vier möglichen Strategien verwendet:
 
<source lang="java5">
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private String id;
</source>
 
:AUTO Überlässt der JPA-Implementierung die Wahl des Verfahrens, i.d.R. IDENTITY oder SEQUENCE. Die Wahl ist sowohl provider- als auch datenbankabhängig.
:IDENTITY Verwendet die Datenbankfunktionalität einer selbstinkrementierenden Spalte.
:TABLE Verwendet eine zusätzliche Tabelle zur Verwaltung des zuletzt generierten Primärschlüssels.
:SEQUENCE Verwendet die Datenbankfunktionalität einer Sequence.
 
'''Problematisch ist die Verwendung einer Generatorart, die vom Datenbanksystem nicht unterstützt wird, z.B. bei Oracle IDENTITY'''
 
=== Sequenz-Generatoren ===
<source lang="java5">
  @Id()
  @GeneratedValue(generator="system-uuid")
  @TypeConverter(name = "stringToByteArray", dataType = byte[].class, objectType = String.class)
  @Convert("stringToByteArray")
  @Column(name = "ID", columnDefinition = "RAW(16) NOT NULL")
  private String id = null;
</source>
 
== Entity-Manager ==
Alle persistierenden Objekte (Persistenzkontext) einer JPA-Anwendung werden durch '''Entity-Manager''' verwaltet. Jeder Entity-Manager wird durch eine Fabrik, das Interface '''EntityManagerFactory''', erzeugt. Die Konfiguration aller Entity-Manager einer solchen Fabrik erfolgt über ein ''Persistence Unit'' mit Hilfe der Konfigurationsdatei '''persistence.xml'''.
 
Bei der Erzeugung und Verwendung des Entity-Managers muss zwischen Java-SE- und Java-EE-Anwendungen differenziert werden. In einer Java-SE-Anwendung wird ein (anwendungsverwalteter) Entity-Manager programmatisch erzeugt. In der verwalteten Umgebung eines Java-EE-Application-Servers erfolgt die Erzeugung des (container-verwalteten) Entity-Managers durch den Applicationserver.
 
<source lang="java5">
// JAVA-SE
  EntityManagerFactory emf = Persistence.createEntityManagerFactory("demo");
  EntitManager em = emf.createEntityManager();
 
  // Mit dem Persistenzkontext arbeiten...
 
  em.close();
  emf.close();
 
// -------------------------------------------------------------------------
 
// Glassfish per Injection
  private static final String PERSISTENCE_UNIT_NAME = "demobase";
 
  @PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
  private EntityManager manager;
</source>
 
In einer Java-SE-Anwendung werden '''Resource-Local'''-Transaktionen verwendet, die explizit durch die Anwendung kontrolliert werden müssen. Die Schnittstelle zur Transaktionsverwaltung ist das Interface '''EntityTransaction'''. Die Reihenfolge der Aufrufe ist nur in Grenzen vorgegeben. Der JPA-Provider kann auch vor der commit()-Methode in die Datenbank schreiben, der spätmöglichste ist direkt vor commit(). Das Schreiben kann durch ''em.flush()'' explizit erzwungen werden.


<source lang="java5">
<source lang="java5">
...
  // Möglichkeit 1
  EntitManager em = ...
 
  // Starten einer Transaktion
  em.getTransaction().begin();
  DemoKlasse demo = new DemoKlasse("Hallo");
  // Persistieren
  em.persist(demo);
  // Ganze Transaktion abwickeln
  em.getTransaction().commit();
  // Möglichkeit 2 (ÄQUIVALENT!)
  DemoKlasse demo = new DemoKlasse("Hallo");
  // Persistieren
  em.persist(demo);
  // Starten einer Transaktion
  em.getTransaction().begin();
  // Ganze Transaktion abwickeln
  em.getTransaction().commit();
...
</source>
=== Operationen auf Entities ===
Zum Laden der Entities gibt es zwei Methoden, find(...) und getReference(...). Aus Kompatibilitätsgründen der verschiedenen JPA-Providern, ist die erste vorzuziehen.
<source lang="java5">
  // Primitiver Datentyp oder zusammengesetzter Schlüssel sind beide möglich
  PrimaerSchluesselKlasse primaerschluessel = new PrimaerSchluesselKlasse("default");
  // null oder DemoKlasse
  DemoKlasse demo = em.find(DemoKlasse.class, primaerschluessel);
  // --- verhindern von Speichern in der Datenbank mit EclipseLink ---
  em.getTransaction().begin();
  Map<String, Object> map = new HashMap<String, Object>();
  map.put("eclipselink.read-only", true);
  DemoKlasse demo = em.find(DemoKlasse.class, primaerschluessel, map);
  // irgendetwas machen, z.B. Änderungen nachfragen
  em.refresh(demo);
  em.getTransaction().commit();


</source>
</source>


Für das Speichern ist die Methode persist(...) und für das Löschen die Methode remove(...) verantwortlich.
Quellen:
Quellen:
<references />
<references />

Aktuelle Version vom 3. März 2016, 10:16 Uhr

JPA ist eine Spezifikation, die von einem JPA-Provider implementiert werden muss. Die bekanntesten sind:

Entitäten

Die Annotation @Entity macht aus der gewöhnlichen, nicht persistenten Java-Klasse eine persistente Entity. Mit implements Serializable wird das Objekt serialisierbar, was z.B. für die Verwendung bei RMI benötigt wird.

Die Instanzvariable id ist mit @Id annotiert und wird damit von JPA als Primärschlüsselvariable verwendet. Ohne weitere Annotation muss die Anwendung dafür Sorge tragen, dass die Entity vor dem Persistieren mit einem gültigen, also noch nicht vergebenen Primärschlüssel versorgt wird.

import javax.persistence.*;

@Entity
public class DemoKlasse {

  @Id
  private String id;
  private String param;

  public DemoKlasse() {
     // Hibernate und OpenJPA bieten eigene Generatoren an
     this.id = UUID.randomUUID().toString();
  }

  public DemoKlasse(String param) {
     this();
     this.param = param;
  }

}

Die Generierung des Primärschlüssel kann auch automatisch erfolgen. Dazu wird die Annotation @GeneratedValue gefolgt von einer der vier möglichen Strategien verwendet:

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private String id;
AUTO Überlässt der JPA-Implementierung die Wahl des Verfahrens, i.d.R. IDENTITY oder SEQUENCE. Die Wahl ist sowohl provider- als auch datenbankabhängig.
IDENTITY Verwendet die Datenbankfunktionalität einer selbstinkrementierenden Spalte.
TABLE Verwendet eine zusätzliche Tabelle zur Verwaltung des zuletzt generierten Primärschlüssels.
SEQUENCE Verwendet die Datenbankfunktionalität einer Sequence.

Problematisch ist die Verwendung einer Generatorart, die vom Datenbanksystem nicht unterstützt wird, z.B. bei Oracle IDENTITY

Sequenz-Generatoren

  @Id()
  @GeneratedValue(generator="system-uuid")
  @TypeConverter(name = "stringToByteArray", dataType = byte[].class, objectType = String.class)
  @Convert("stringToByteArray")
  @Column(name = "ID", columnDefinition = "RAW(16) NOT NULL")
  private String id = null;

Entity-Manager

Alle persistierenden Objekte (Persistenzkontext) einer JPA-Anwendung werden durch Entity-Manager verwaltet. Jeder Entity-Manager wird durch eine Fabrik, das Interface EntityManagerFactory, erzeugt. Die Konfiguration aller Entity-Manager einer solchen Fabrik erfolgt über ein Persistence Unit mit Hilfe der Konfigurationsdatei persistence.xml.

Bei der Erzeugung und Verwendung des Entity-Managers muss zwischen Java-SE- und Java-EE-Anwendungen differenziert werden. In einer Java-SE-Anwendung wird ein (anwendungsverwalteter) Entity-Manager programmatisch erzeugt. In der verwalteten Umgebung eines Java-EE-Application-Servers erfolgt die Erzeugung des (container-verwalteten) Entity-Managers durch den Applicationserver.

// JAVA-SE
  EntityManagerFactory emf = Persistence.createEntityManagerFactory("demo");
  EntitManager em = emf.createEntityManager();

  // Mit dem Persistenzkontext arbeiten...

  em.close();
  emf.close();

// -------------------------------------------------------------------------

// Glassfish per Injection
  private static final String PERSISTENCE_UNIT_NAME = "demobase";

  @PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
  private EntityManager manager;

In einer Java-SE-Anwendung werden Resource-Local-Transaktionen verwendet, die explizit durch die Anwendung kontrolliert werden müssen. Die Schnittstelle zur Transaktionsverwaltung ist das Interface EntityTransaction. Die Reihenfolge der Aufrufe ist nur in Grenzen vorgegeben. Der JPA-Provider kann auch vor der commit()-Methode in die Datenbank schreiben, der spätmöglichste ist direkt vor commit(). Das Schreiben kann durch em.flush() explizit erzwungen werden.

...

  // Möglichkeit 1

  EntitManager em = ...
  
  // Starten einer Transaktion
  em.getTransaction().begin();
  DemoKlasse demo = new DemoKlasse("Hallo");

  // Persistieren
  em.persist(demo);

  // Ganze Transaktion abwickeln
  em.getTransaction().commit();


  // Möglichkeit 2 (ÄQUIVALENT!)

  DemoKlasse demo = new DemoKlasse("Hallo");

  // Persistieren
  em.persist(demo);

  // Starten einer Transaktion
  em.getTransaction().begin();

  // Ganze Transaktion abwickeln
  em.getTransaction().commit();

...

Operationen auf Entities

Zum Laden der Entities gibt es zwei Methoden, find(...) und getReference(...). Aus Kompatibilitätsgründen der verschiedenen JPA-Providern, ist die erste vorzuziehen.

  // Primitiver Datentyp oder zusammengesetzter Schlüssel sind beide möglich
  PrimaerSchluesselKlasse primaerschluessel = new PrimaerSchluesselKlasse("default");

  // null oder DemoKlasse
  DemoKlasse demo = em.find(DemoKlasse.class, primaerschluessel);

  // --- verhindern von Speichern in der Datenbank mit EclipseLink ---
  em.getTransaction().begin();
  Map<String, Object> map = new HashMap<String, Object>();
  map.put("eclipselink.read-only", true);
  DemoKlasse demo = em.find(DemoKlasse.class, primaerschluessel, map);

  // irgendetwas machen, z.B. Änderungen nachfragen
  em.refresh(demo);
  em.getTransaction().commit();

Für das Speichern ist die Methode persist(...) und für das Löschen die Methode remove(...) verantwortlich. Quellen:

  1. [1] Hibernate JPA-Provider
  2. [2] EclipseLink Refernzimplementierung von JPA
  3. [3] OpenJPA JPA-Provider