Java Persistence API (JPA) © Christoph Knabe Beuth-Hochschule Berlin FB VI 12.06.2014 Gliederung Geschichte Java Persistenz Mapping-Annotationen Assoziations-Annotationen Vererbungs-Annotationen Konfiguration Entity-Zustände Persistence bis EntityManager.persist Queries Fazit Geschichte Java Persistenz Java Database Connectivity (JDBC) Erstmalig 1997 in JDK 1.1 http://docs.oracle.com/javase/7/docs/technotes/guides/jdbc Vorbild war ODBC Bietet Ausführung von Strings als SQL an. — Großer Abstand Objekt ↔ Tabellenzeile + Alle Java-Persistenzframeworks basieren darauf. Enterprise Java Beans (EJB) Entity Beans 1997 durch IBM, 1999 übernommen durch Sun — schwergewichtig: Entity muss bestimmte Interfaces implementieren. — schwergewichtig: Läuft nur in EJB-Container. — Deprecated ab EJB 3.1 (2009) Geschichte II Java Data Objects (JDO) Erstmalig 2002 als JSR 12 http://db.apache.org/jdo/specifications.html + Plain Old Java Objects (POJO) persistierbar: Es müssen keine Interfaces implementiert werden. + Gut in Speicherung von Objektgraphen — Erfordert Byte-Code-Enhancing aller Entity-Klassen. Java Persistence API (JPA) Erstmalig 2006 als Teil von JSR 220 (EJB 3.0) http://db.apache.org/jdo/specifications.html + Plain Old Java Objects (POJO) persistierbar: Es müssen keine Interfaces implementiert werden. Vorbilder waren Hibernate und TopLink. + Kommt ohne Byte-Code-Enhancing aus. Mapping-Annotationen @Entity vor Klasse: Persistierbar @Table(name="tabellenname"): Wahl eines Tabellennamens Attribut-Mapping durch Annotation entweder vor Attributen oder Gettern @Id vor Primärschlüssel-Attribut @GeneratedValue vor diesem: Wert wird durch JPA vergeben. @Enumerated(EnumType.strategie) vor enum-Attribut wählt - Speicherung als String oder - Speicherung als Ordnungszahl. JSR-303 Bean Validation möglich, z.B. - @NotEmpty: Weder null noch Länge 0 - @Size(min=..., max=...): Für Strings, Collections und Arrays Assoziations-Annotationen @Embedded vor Attribut Referenziertes Objekt wird in derselben Tabellenzeile gespeichert. @OneToOne vor Attribut Referenziertes Objekt wird in eigener Tabelle gespeichert. @ManyToOne vor Referenz-Attribut Lädt referenziertes Objekt mit eigenem. + Ladeaufwand proportional Schreibaufwand: verständlich @OneToMany vor Collection-Attribut mit @JoinColumn(name = "fremdschlüssel"): Lädt in Collection alle Zeilen, die mittels fremdschlüssel auf aktuelles Objekt zeigen. Bidirektionale Verzeigerungen möglich - Risiko, zu viel zu laden (z.B. bei m:n-Assoziation) - Gegenrichtung muss in Java redundant gepflegt werden. Besser: unidirektional (@ManyToOne) mit Suche bei Bedarf. Vererbungs-Annotationen @Inheritance vor Basisklasse SINGLE_TABLE-Strategie: Objekte der Basisklasse und aller Unterklassen werden in einer Tabelle gespeichert ⇒ Diskriminator-Spalte nötig. Konfigurierbar per @DiscriminatorColumn, @DiscriminatorValue — Platzverschwendung durch unbelegte Zellen @Inheritance(strategy=InheritanceType.JOINED) Jede Klasse (Basis, Unter) wird in eigener Tabelle gespeichert. Zusammenführung über gemeinsamen Primärschlüssel. + Änderung einer Klasse ändert nur eigene Tabelle — Objekt laden aufwändig wegen Join. @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) Eine Tabelle je konkrete Klasse. — Polymorphe Query aufwändig durch Union — Polymorphe Assoziation zur Basisklasse unmöglich! Beispiele: https://www.assembla.com/code/lehrkraftnews/git/nodes/master/fb6/java/fb6/_any/db/DbObject.java https://www.assembla.com/code/lehrkraftnews/git/nodes/master/fb6/java/fb6/lehrkraftnews/db Konfiguration JPA-Provider als Maven-Dependency angeben Bsp.: https://www.assembla.com/code/lehrkraftnews/git/nodes/master/fb6/pom.xml JPA konfigurieren In META-INF/persistence.xml angeben: Provider-Klasse, zu persistierende Klassen/Pakete, Properties für JDBC-Verbindung, DB-Dialekt, SQL-Generierung, Schema-Update Bsp.: https://www.assembla.com/code/lehrkraftnews/git/nodes/master/fb6/java/META-INF/persistence.xml Entity-Zustände Transient: vorübergehend Nachdem Objekt mit new erzeugt wurde. Hat noch keine ID. Nicht in Datenbank. Persistent: dauerhaft Wird erreicht durch Übergabe an EntityManager.persist. Nur innerhalb einer Transaktion möglich. Änderungen werden bei commit in die Datenbank gespeichert. Detached: losgelöst Nach einer Transaktion. Objekt hat noch seine ID, zu der es wahrscheinlich noch eine Zeile in der DB gibt. Ein Detached-Objekt kann durch EntityManager.merge wieder mit der Datenbank verbunden (Persistent) werden. Removed: gelöscht Durch EntityManager.remove Von Persistence zu persist EntityManagerFactory erzeugen (teuer): final EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistenceUnitName"); EntityManager erzeugen (billig): final EntityManager em = emf.createEntityManager(); Transaktion beginnen: em.getTransaction().begin(); Objekt erstellen und unter JPA-Verwaltung stellen: final Person person = new Person("Müller", "Manfred"); em.persist(person); Transaktion beenden: em.getTransaction().commit(); bzw. em.getTransaction().rollback(); Queries JPQL-Queries (polymorph) - Sollte generisch verpackt werden! final Query query = em.createQuery("SELECT p FROM person WHERE name = :name"); query.setParameter("name", "Müller"); final List<Object> personen = query.getResultList(); JPA 2 Criteria API Queries (typsicher) final CriteriaBuilder cb = em.getCriteriaBuilder(); final CriteriaQuery<Person> cq = cb.createQuery(Person.class); final Root<Person> rp = cq.from(Person.class); final ParameterExpression<String> p = cb.parameter(String.class); cq.select(rp).where(cb.eq(rp.get("name", p)); final TypedQuery<Person> pq = em.ceateQuery(cq); pq.setParameter(p, "Müller"); final List<Person> personen = pq.getResultList(); Fazit + Standard: Standardisiertes API für Speicherung von Objekten in Relationalen DBen + Erfahrung: Basiert auf umfangreichen Erfahrungen (positiv: Hibernate, negativ: EJBCMP) + Austauschbarkeit: Wegen Aufsetzen auf dem Standard JDBC kann sowohl der PersistenceProvider als auch das DBMS ausgetauscht werden. Mehr: Ausführliche Folien unter http://www.kunkelgmbh.de/jpa/internal/JPA_mit_Hibernate.pdf