Java Persistence API
Willemers Informatik-Ecke

ORM: Object Relational Mapping

Eine Java-Bean besteht aus Attributen, den passenden Getter- und Setter-Methoden und einem öffentlichen Konstruktor. Oft werden die Inhalte einer Bean in eine Datenbanktabelle gleicher Struktur geschrieben oder gelesen. So ist es naheliegend, eine Bean mit einer Datenbanktabelle zu verheiraten.

Die Zuordnung und Übertragung von Objekten zu Datenbankeinträgen wird als Object Relational Mapping (ORM) bezeichnet. Das Ziel ist es, nicht mehr jedes einzelne Attribut anzufassen, sondern Objekte mit Tabellen zu synchronisieren.

Relation

Bei einer 1:n-Beziehung steht eine Zeile einer Tabelle im Verhältnis zu mehreren Zeilen einer anderen Tabelle. So kann ein Kunde mehrere Bestellungen erhalten. In der Datenbank wird dies erreicht, indem die Tabelle Bestellung die ID des Kunden enthält.

Dieses Verhalten könnte man in der Bean Bestellung die ID der Bean Kunde aufnimmt. Allerdings ist das Suchen von nach der ID in Beans nicht so einfach. Darum ist es wesentlich sinnvoller, das die Bestellung gleich den Kunden als Attribut erhält. Die Referenz auf den Kunden ist auch nicht größer als die ID, eröffnet aber programmtechnisch wesentlich mehr Möglichkeiten.

Auf der Gegenseite kann die Bean Kunde eine Liste von Bestellungen enthalten. Auch dies ist eine Referenz.

Java Persistance API

Die JPA ist eine Standardisierung der Datenbankzugriffe, die auf dem Object Relational Mapping (ORM) basiert. Die Referenzimplementierung ist EclipseLink und ist Bestandteil des Glassfish. Eine solche Implementierung wird auch Persistence Provider genannt.

Die JPA gehört nicht zum Standard-Umfang der Java Standard Edition oder Tomcat. In diesen Fällen muss die Bibliothek des Persistence Providers eingebunden werden. Diese JAR wird unter Eclipse bei Anlegen eines JPA-Projekts gleich angefordert und bei Bedarf automatisch heruntergeladen.

Es gibt mehrere Persistence Provider. Zu den bekanntesten gehören folgende:

Eclipse-Unterstützung für JPA-Anwendungen

Eclipse stellt für Standalone-Anwendungen ein eigenes JPA-Projekt zur Verfügung. Es ist erzeugt, indem New | JPA Project ausgewählt wird. In dem folgenden Dialog werden ähnliche Einstellungen gewählt wie bei einer JEE-Anwendung, die mit New | Dyncamic Web Project angelegt wird. JPA bietet unter Eclipse eine eigene Perspektive. Dazu werden weitere Fenster wie auf der linken Seite ein Data Source Explorer und auf der rechten Seite die JPA-Details angezeigt.

JPA-Unterstützung durch den Application Server

Ein vollwertiger Application Server enthält bereits einen JPA Persistence Provider. Die Datenbank kann dann durch den Administrator des Application Servers konfiguriert werden. Dieses Verfahren nennt sich JTA, da in diesem Fall der Application Server auch die Verwaltung der Transaktionen übernimmt.

Eine JEE-Anwendung kann aber genau wie eine Java-SE-Anwendung die Konfiguration der JPA selbst übernehmen. Dies wird als Resource Local im Eclipse konfiguriert.

persistence.xml

Die Datei persistence.xml definiert die Datenbankkonfiguration einer JPA-Anwendung. Bei Eclipse befindet sie sich im Projektordner JPA Content. Wird das Projekt allerdings durch einen Dateimanager oder per Terminal geöffnet, befindet sie sich unterhalb des Projektverzeichnis in src/META-INF.

Die Datei definiert die Persistence Unit, die im Programm bei der Erstellung der EntityManagerFactory als Kennung für die Datenbank-Ressource aufgegriffen wird. Eclipse bietet neben der XML-Darstellung unter der Lasche Source einige Dialoge, die die Konfiguration vereinfachen.

Die untenstehende persistence.xml ist eine JTA-Variante. Sie ist übersichtlich groß, da die Datenbank im Application Server konfiguriert wird. Der durch <jta-data-source> eingeschlossene Kennung bezieht sich auf die Konfiguration des Application Servers.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="gastPU" transaction-type="JTA">
    <jta-data-source>jdbc/__meineDB</jta-data-source>
        <class>Gast</class>
    </persistence-unit>
</persistence>
Wird die Datenbankkonfiguration innerhalb der Anwendung, wie bei Java-SE-Anwendungen oder JEE-Anwendungen, die JTA nicht in Anspruch nehmen, wird statt JTA das Schlüsselwort RESOURCE_LOCAL im Klappmenü Transaction type unter der Lasche Connection gewählt.

In diesem Fall wird der Link Populate from Connection auf der gleichen Seite anklickbar und ermöglicht die Übernahme der Konfiguration aus der zuvor erstellten Konfiguration des JPA-Projekts aus Eclipse. Das Ergebnis kann folgendermaßen aussehen.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="gastPU" transaction-type="RESOURCE_LOCAL">
        <class>Gast</class>
        <properties>
          <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
          <property name="javax.persistence.jdbc.url" value="jdbc:derby:/home/arnold/databases/simpleDb;create=true" />
          <property name="javax.persistence.jdbc.user" value="test" />
          <property name="javax.persistence.jdbc.password" value="test" />

          <!-- EclipseLink should create the database schema automatically -->
          <property name="eclipselink.ddl-generation" value="create-tables" />
          <property name="eclipselink.ddl-generation.output-mode" value="database" />
        </properties>
    </persistence-unit>
</persistence>
Als <class> werden alle Klassen eingetragen, die als Entity-Klassen für den Zugriff auf die Datenbanktabellen eingesetzt werden. Diese können unter Eclipse unter der Lasche General in der Liste Managed Classes durch die nebenstehenden Buttons ausgewählt werden.

Die letzten beiden <property>-Zeilen ermöglichen es, aus den Entity-Klassen die Datenbanktabellen generieren zu lassen. Das kann bei einfacheren Anwendungen sinnvoll sein, damit die Anwendung bei einer neuen Installation automatisch die benötigte Datenbank generiert.

Entity

JPA bildet eine Datenbanktabellenzeile auf ein Java-Objekt ab. Ein solches Objekt wird als Entity bezeichnet und ist eigentlich eine gewöhnliche Java-Klasse deren Attribute den Feldern einer Datenbanktabelle entsprechen. Damit JPA die Klasse als Entity erkennt, wird sie mit der Annotaion @Entity gekennzeichnet. Ebenfalls zwingend ist ein Attribut, das auf den Primärschlüssel der Tabelle verweist und mit @Id markiert wird. Die Klasse sollte darüber hinaus Serializable implementieren und benötigt einen öffentlichen Konstruktor.

import java.io.Serializable;

@Entity(name="Gast")
public class Gast implements Serializable {
    @Id
    @GeneratedValue
    private Long id;
    @Column
    private String name;
    @Column
    private String mail;

    public Gast() {
    }

    public Long getId() { return id; }
    public String getName() { return name; }
    public String getMail() { return mail; } 
    public void setId(Long id) { this.id = id; } 
    public void setName(String name) { this.name = name; }
    public void setMail(String mail) { this.mail = mail; }
}

Die Annotation @GeneratedValue bezeichnet einen Primärschlüssel, der von der Datenbank automatisch generiert wird, beispielsweise per Autoinkrement.

Die Attribute, die auf Spalten der Datenbanktabelle können mit @Column markiert werden. Dadurch ist es auch möglich, Spalten mit ungleichen Namen zu referenzieren. Tatsächlich werden alle Attribute automatisch als Referenzen auf Tabellenspalten aufgefasst, sofern sie nicht mit @Transient markiert werden.

Man kann Entities wie oben beschrieben als normale Klassen erstellen. Darüber hinaus bietet Eclipse die Möglichkeit aus der erstellten Datenbank die Entities automatisch erstellen zu lassen. Dazu klicken Sie das Projekt mit der rechten Maustaste an und wählen: New | JPA Entity from Table. Die folgenden Dialoge ermöglichen Ihnen weitere Einflussnahme auf die Erstellung.

Annotationen

Foreign-Key in Entity

Eine Referenz der Entity Buchung auf eine andere Tabelle Gast (Foreign key), erfolgt über eine Collection-List (java.util) und @OneToMany.
@OneToMany(mappedBy="gast")
private List buchungen;
Die referenzierte Klasse Gast muß nun ein Attribut mit dem Namen aufweisen, dass in OneToMany. Es muss vom Typ der referenzierenden Entity sein und wird mit @ManyToOne eingeleitet. @JoinColumn ist optional.
@ManyToOne
@JoinColumn(name = "gastid")
private Gast gast;

Der EntityManager

Der EntityManager sorgt dafür, dass die Daten zwischen Datenbanktabelle und den Java-Objekten hin und her gehen.

Wenn Sie die Transaktionsverwaltung dem Application Server überlassen, dann erhalten Sie den EntityManager per Inject:

@PersistenceContext(unitName = "gastPU")
private EntityManager entityManager;
In allen anderen Fällen erzeugen Sie den EntityManager durch eine EntityManagerFactory. Die Factory wiederum erhält den Namen der Persistence Unit, die in der Datei persistence.xml beschrieben wird.
EntityManagerFactory factory = Persistence.createEntityManagerFactory("gastPU");
EntityManager entityManager = factory.createEntityManager();

Anfragen

Über die Methode createQuery können SQL-Anfragen an die Datenbank gestellt werden. Als Parameter erhält sie den SQL-Befehl. Der Rückgabewert ist von der Klasse Query.

Über dieses Query-Objekt können Sie die Methode getResultList aufrufen, die Ihnen eine Liste aller gefundenen Elemente liefert.

Query query = manager.createQuery("select t from Gast t");
List liste = query.getResultList();
for (Gast person : liste) {
    System.out.println(person.getName()+"---"+person.getId());
}

Änderungen an der Datenbank

Bei den meisten Datenbanken erfolgen Änderungen im Zusammenhang mit einer Transaktion. Eine Transaktion garantiert einen konsistenten Zustand. Sollte es nach der Transaktion nicht möglich sein, einen konsistenten Zustand zu erreichen, wird die Datenbank in den vorherigen Zustand überführt.

Arbeitet JPA mit JTA, so übernimmt der Application Server die Transaktionsverwaltung. Wenn Sie allerdings Resource Local verwenden, müssen Sie die Transaktionen selbst organisieren.

Sinnvollerweise werden begin und commit mit den Datenbankbefehlen in einen try-Block gesetzt. Wird mit catch eine Exception gefangen, wird rollback ausgelöst.

JPA-Anwendung

Das folgende Beispiel ist eine JPA-Konsolenanwendung.
import java.util.List;

public class Main {
    
    static EntityManagerFactory factory = null;
    
    private static Gast createGast (
            String name, String mail) {
        Gast gast = new Gast();
        gast.setName(name);
        gast.setMail(mail);
        return gast;
    }
    
    private void speichere(EntityManager manager, Gast gast) {
        manager.getTransaction().begin();
        manager.persist(gast);
        manager.getTransaction().commit();
    }
    
    private void zeige(EntityManager manager) {
        TypedQuery query = manager.createQuery("select t from Gast t", Gast.class);
        List liste = query.getResultList();
        for (Gast gast : liste) {
            System.out.println(gast);
        }
    }
    
    private void suche(EntityManager manager, String name) {
        TypedQuery query = manager.createQuery(
                "select +"
                + "t from Gast t where t.name=:name",
                Gast.class);
        query.setParameter("name", name);
        List liste = query.getResultList();
        for (Gast gast : liste) {
            System.out.println(gast);
        }
    }
    
    public Main() {
        EntityManager manager = factory.createEntityManager();
        // Testdaten erzeugen
        speichere(manager, createGast("Bedman", "bed@man"));
        speichere(manager, createGast("Sabber", "sbbr@man"));
        // Daten auslesen und anzeigen
        zeige(manager);
        suche(manager, "Bedman");
        manager.close();
    }
    
    public static void main(String[] args) {
        factory = Persistence.createEntityManagerFactory("gastPU");
        new Main();
        factory.close();
    }
}

Zu JPA siehe auch: Christian Dürr: Grundlagen der JPA

JEE-Literatur

Marcus Schießer, Martin Schmollinger: Workshop Java EE 7

Etwas abstrakter: Dirk Weil: Java EE 7: Enterprise-Anwendungsentwicklung leicht gemacht

Das Buch enthält über 100 Seiten über JEE: Arnold Willemer: Java Alles-in-einem-Band für Dummies