4. Java Persistence API (JPA) • • • • • • • • • Problem: OO in relationale DB Idee des Persistence Mapper Einfache Entity-Klasse Lebenslauf eines Entity-Objekts Umsetzung von 1:N- und M:N-Relationen Geordnete Daten Anfragen Vererbung Validierung Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 142 Literatur • JPA 2.1: Sun Microsystem: JSR 338: JavaTM Persistence 2.1 http://jcp.org/en/jsr/detail?id=338 (JavaPersistence.pdf ist der Standard; trotzdem sehr gut lesbar!) Auch für Teile der weiteren Vorlesung • C. Bauer, G. King, Java Persistence with Hibernate, Manning, Greenwich (USA) 2007 • M. Keith, M. Schincariol, Pro EJB 3 - Java Persistence API, Apress, Berkeley (USA), 2006 Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 143 Klassische Wege zur Verbindung SW und DB DB werden meist nicht alleine entwickelt, sie müssen mit umgebender SW integriert werden. Es gibt verschiedene Ansätze, die gerade bei der Anbindung von OO-SW relevant sind: - SW wird (z.B: mit PL/SQL) in der Datenbank entwickelt (hierzu gibt es auch objektorientierte Ansätze), externe SW kann auf Prozeduren und Funktionen zugreifen. - SQL-Aufrufe werden direkt in die SW eingebettet (Embedded SQL) bzw. Aufrufe werden durch ein einfaches Framework (z.B. JDBC, SQLJ) gekapselt. (Frage: wie bekomme ich Objekte in die DB?) - DB-Entwicklung und SW-Entwicklung wird eng miteinander verzahnt, hierzu stehen ausgereifte DevelopmentFrameworks zur Verfügung (EJB, .NET) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 144 Nutzung relationaler DB in OO-Programmen Oberfläche + Geschäftslogik • Bisheriger Ansatz: relationale DB vorhanden, wird an OO-Programm angeschlossen. DB-Zugriffsschicht • Was ist, wenn DB zusammen mit OOProgramm entwickelt wird? • erster Ansatz: warum nicht genau so vorgehen z.B. JDBC relationale Datenbank Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 145 Lagerverwaltung (ein kleines OO-Modell) beinhaltet Lagerraum -nummer : String -artikel 1 * +verkaufswertBerechnen() : float - artikel bezeichnet eine Collection von Produkten in einem Lagerraum - mit verkaufspreis() wird die Mehrwertsteuer eingerechnet, die bei Büchern anders sein soll. Lebensmittel -verfallsdatum : Date +verlustAm(stichtag : Date) : float Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker Produkt -name : String -lagermenge : int -preis : float +Produkt() : Produkt +verfuegbareAnzahl() : int +verkauft(anzahl : int) : void +einlagern(anzahl : int) : void +verkaufspreis() : float Buch +verkaufspreis() : float 146 Aufgabe und Ansatz • Objekte der im Lagerhaltungsmodell vorgestellten Klassen sollen persistent in einer relationalen DB abgespeichert werden • Bei der Anbindung an die Datenbank sollen möglichst viele OO-Errungenschaften (Kapselung, Vererbung, Polymorphismus) übernommen werden • Grundidee: Das Klassendiagramm kann ohne Methoden als ER-Diagramm gelesen werden, es beinhaltet eine 1:N und zwei 1:C Beziehungen • Anmerkung: Dieser Übersetzungsansatz war ein beliebtes Tummelfeld von Programmierern ☺, es wird nur ein möglicher Ansatz vorgestellt Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 147 Umgang mit Kapselung • Objekte der Klasse Produkt können leicht in folgende Tabelle übersetzt werden. Produkt OID name lagermenge preis Produkt -name : String -lagermenge : int -preis : float +Produkt() : Produkt +verfuegbareAnzahl() : int +verkauft(anzahl : int) : void +einlagern(anzahl : int) : void +verkaufspreis() : float • zusätzlicher Primary Key vergeben, falls name nicht eindeutig • Kapselungsidee wird aufgegeben, da man über die Datenbank immer direkt auf die Attribute zugreifen kann, nur durch Mehraufwand einschränkbar: – Lösung auf der Zugriffsebene im OO-Programm (umgehbar) – Spezielle Nutzer und Rechte, Nutzung von Views (Aufwand) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 148 Anmerkungen • Typischerweise werden Klassenvariablen nicht mit den jeweiligen Objekten abgespeichert, sie müssen explizit behandelt werden • Es stellt sich generell die Frage, welche zusätzlichen Informationen in welchen Tabellen verwaltet werden sollen Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 149 Umgang mit Vererbung • Vererbung kann durch eine zusätzliche Tabelle mit einer Referenz implementiert werden, dabei ist OID Schlüssel und Fremdschlüssel aus Produkt Lebensmittel OID Produkt Lebensmittel -verfallsdatum : Date +verlustAm(stichtag : Date) : float verfallsdatum • Für jede Vererbungsebene wird eine neue Tabelle benötigt, was den Zugriff aufwändig macht • Durch die vorgeschlagene Struktur wird die Sichtweise „ein Lebensmittel ist ein Produkt“ unterstützt Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 150 Umgang mit Polymorphismus • Wie funktioniert Polymorphismus? public float verkaufswertBerechnen(){ float ergebnis=0.0f; for(Produkt tmp:artikel) ergebnis= ergebnis+ tmp.verfuegbareAnzahl()*tmp.verkaufspreis(); return ergebnis; } • In der bisherigen relationalen Übersetzung muss sichergestellt werden, dass das richtige Objekt geladen wird • Die genaue Klassendefinition bekommt man im vorgestellten Ansatz nur, wenn man alle zu Unterklassen gehörigen Tabellen untersucht, ob die OID vorkommt • Alternativ kann man sich die genaue Klassenzugehörigkeit in einem weiteren Attribut der Tabelle Produkt merken Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 151 Umgang mit Objektidentität • Objektidentität in der Datenbank wird durch den Primary Key garantiert • Wird das Objekt aus der Datenbank gelesen, ist der Nutzer verantwortlich, dass kein zweites Exemplar des gleichen Objekts geladen wird – Alternative: Beim ersten Herauslesen wird ein Flag (ein zusätzliches Attribut) gesetzt, dass sich das Exemplar außerhalb der DB befindet (Ansatz fordert viel Programmierdisziplin, insbesondere bei der Programmterminierung) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 152 Umgang mit Beziehungen zwischen Objekten • Exemplarvariablen, die Collections oder andere nicht triviale Objekttypen enthalten, müssen explizit durch eine oder mehrere Relationen oder Erweiterungen von Tabellen modelliert werden (der Ansatz ist vom Übergang vom ER-Diagramm zur Tabellenstruktur bekannt) beinhaltet Lagerraum -artikel 1 * Produkt Produkt OID name lagermenge preis lagerraum_OID • Frage: Warum passt die skizzierte Lösung eigentlich nicht zum UML-Diagramm? Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 153 Bearbeiten von Objekten • Wir haben gesehen, dass einfache Objekte häufig über mehrere Tabellen verteilt werden, d.h. zum Einlesen und Bearbeiten sind relativ aufwändige SQL-JOINS notwendig • Weiterhin muss das OO-Programm die eindeutige Referenz eines Objektes in der relationalen DB kennen • Beispiel: Einlesen eines Lebensmittels, das an eine Variable mit Namen „banane“ gebunden war SELECT name, lagermenge, preis, verfallsdatum FROM Identifikation, Lebensmittel, Produkt WHERE Identifikation.Name = 'banane' AND Identifikation.OID = Produkt.OID AND Produkt.OID = Lebensmittel.OID Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 154 Zwischenfazit • Selbst bei Beispielen, in denen der Einsatz relationaler Datenbanken sinnvoller Standard ist (Lagerverwaltung), wird die Verknüpfung eines OO-Programms mit relationaler Datenhaltung aufwändig • Die Nutzung relationaler Datenbanken in OO-Programmen sollte immer über eine DB-Kapselung, die den Zugriff auf die DB regelt, erfolgen • Für den Kapselungsansatz stehen verschiedene Produkte zur Verfügung (z. B. Hibernate, EclipseLink(TopLink)) , JPA spezifiziert gemeinsames Interface • Verwandte Ansätze: direkte Nutzung einer OO-Datenbank, Programmierung mit JDO Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 155 Konzept von Persistence-Mappern • Persistence-Mapper (PM) muss wissen, welche Objekte persistiert werden sollen -> markiere Klassen, deren Objekte verwaltet werden sollen • Persistence-Mapper muss Beziehungen zwischen Objekten kennen • Ablauf: Nutzer benutzt Objekt unter Verwaltung; dann übernimmt PM die Überwachung, führt Änderungen aus, regelt den Zugriff mehrerer Nutzer • Ablauf: Nutzer erzeugt Objekt und muss PM einmal mitteilen, dieses zu verwalten • PM kann selbst regeln, wie Aufgaben realisiert werden (Tabellenstruktur, Transaktionssteuerung) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 156 Beispiel (1/7) package jpa20beispiel1; import javax.persistence.Entity; import javax.persistence.Id; @Entity public class Mitarbeiter { @Id private int minr; private String name; public Mitarbeiter(){} //parameterloser Konstruktor benötigt public Mitarbeiter(int minr, String name) { //erlaubt this.minr = minr; this.name = name; } public public public public int getMinr() {return minr;} void setMinr(int minr) {this.minr = minr;} String getName() {return name;} void setName(String name) {this.name = name;} } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 157 Beispiel (2/7) • persistence.xml liegt im Ordner META-INF (projektartabhängig) • Detaileinstellungen von JPA-Realisierung abhängig (z. B. EclispeLink (basiert auf TopLink), Hibernate, Apache OpenJPA) <?xml <?xml version="1.0" version="1.0" encoding="UTF encoding="UTF="UTF-8"?> <persistence version="2.0" version="2.0" xmlns="http xmlns="http://xmlns.jcp.org/ ="http://xmlns.jcp.org/xml ://xmlns.jcp.org/xml/ xml/ns/ ns/persistence" persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema xmlns:xsi="http://www.w3.org/2001/XMLSchema="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http xsi:schemaLocation="http://xmlns.jcp.org/ ="http://xmlns.jcp.org/xml ://xmlns.jcp.org/xml/ xml/ns/ ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_0.xsd ://xmlns.jcp.org/xml/ns/persistence/persistence_2_0.xsd"> "> http://xmlns.jcp.org/xml/ns/persistence/persistence_2_0.xsd <persistencepersistence-unit name="JPA20Beispiel1PU" name="JPA20Beispiel1PU" transactiontransaction-type="RESOURCE_LOCAL"> <provider> provider> org.eclipse.persistence.jpa.PersistenceProvider </provider </provider> provider> <class>jpa20beispiel1.Mitarbeiter</ class>jpa20beispiel1.Mitarbeiter</class >jpa20beispiel1.Mitarbeiter</class> class> Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 158 Beispiel (3/7) <properties> properties> <property name="javax.persistence.jdbc.url" name="javax.persistence.jdbc.url" value=" value="jdbc:derby ="jdbc:derby:// jdbc:derby://localhost:1527/Spielerei"/> ://localhost:1527/Spielerei"/> <property name=" name="javax.persistence.jdbc.password ="javax.persistence.jdbc.password" javax.persistence.jdbc.password" value=" value="kleuker ="kleuker"/> kleuker"/> <property name=" name="javax.persistence.jdbc.driver ="javax.persistence.jdbc.driver" javax.persistence.jdbc.driver" value=" value="org.apache.derby.jdbc.ClientDriver ="org.apache.derby.jdbc.ClientDriver"/> org.apache.derby.jdbc.ClientDriver"/> <property name=" name="javax.persistence.jdbc.user ="javax.persistence.jdbc.user" javax.persistence.jdbc.user" value=" value="kleuker ="kleuker"/> kleuker"/> <property name=" name="eclipselink.ddl ="eclipselink.ddleclipselink.ddl-generation" value=" value="drop ="dropdrop-andand-createcreate-tables"/> tables"/> </properties </properties> properties> </persistence </persistencepersistence-unit> </persistence </persistence> persistence> Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 159 Beispiel (4/7) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 160 Beispiel (5/7) package jpa20beispiel1; import java.util.List; java.util.List; import javax.persistence.EntityManager; javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; javax.persistence.Persistence; public class Main { private EntityManagerFactory emf = Persistence. createEntityManagerFactory("JPA20Beispiel1PU"); createEntityManagerFactory("JPA20Beispiel1PU"); private EntityManager em = emf.createEntityManager(); emf.createEntityManager(); public void beispieldaten() beispieldaten() { String namen[] namen[] = {"Egon", "Erwin", "Ute", "Aise"}; Aise"}; em.getTransaction(). em.getTransaction().begin ().begin(); begin(); for (int i=0; i<namen.length i<namen.length; namen.length; i++) em.persist( em.persist(new Mitarbeiter(i,namen Mitarbeiter(i,namen[i])); i,namen[i])); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 161 Beispiel (6/7) public void datenZeigen() datenZeigen() { for (Mitarbeiter m : em.createQuery( em.createQuery( "SELECT m FROM Mitarbeiter m",Mitarbeiter.class m",Mitarbeiter.class) Mitarbeiter.class) .getResultList()) getResultList()) { System.out.println( System.out.println(m.getMinr() m.getMinr() + ": " + m.getName()); m.getName()); } } public void schliessen() schliessen() { if (em != null && em.isOpen()) em.isOpen()) {em.close {em.close();} em.close();} if (emf != null && emf.isOpen()) emf.isOpen()) {emf.close {emf.close();} emf.close();} } 1: Egon public static void main(String[] main(String[] args) args) { Main m = new Main(); 2: Erwin m.beispieldaten(); m.beispieldaten(); 4: Aise m.datenZeigen(); m.datenZeigen(); 3: Ute m.schliessen(); m.schliessen(); } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 162 Beispiel (7/7) • Falls keine Tabelle Mitarbeiter existiert, wird diese angelegt Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 163 Suchen und Bearbeiten von einzelnen Objekten public void namenAendern(){ namenAendern(){ int eingabe= eingabe=-1; while( while(eingabe!=0 eingabe!=0) !=0) { System.out.print("Welche System.out.print("Welche Nummer (Ende mit 0): "); eingabe= eingabe=new Scanner(System.in).nextInt Scanner(System.in).nextInt(); nextInt(); Mitarbeiter m = em.find( em.find(Mitarbeiter.class, Mitarbeiter.class, eingabe); eingabe); if(m if(m == null) System.out.println("Witzbold"); System.out.println("Witzbold"); else { System.out.print("Neuer System.out.print("Neuer Name (alt:"+m.getName (alt:"+m.getName()+"): m.getName()+"): "); String name= name=new Scanner(System.in).next Scanner(System.in).next(); next(); EntityTransaction tr = em.getTransaction(); em.getTransaction(); tr.begin(); tr.begin(); m.setName( m.setName(name); name); // em.merge(m); em.merge(m); steht hier üblicherweise, geht ohne tr.commit(); tr.commit(); } } Prof. Dr. 164 }Komponentenbasierte SoftwareEntwicklung Stephan Kleuker Zentrale Klassen • Aufbau der Verbindung EntityManagerFactory emf = Persistence. Persistence. createEntityManagerFactory("JPA20Beispiel1PU"); createEntityManagerFactory("JPA20Beispiel1PU"); – Einrichtung sehr aufwändig, selten neu erstellen • Einrichtung der Verbindung für den Nutzer EntityManager em = emf.createEntityManager(); emf.createEntityManager(); – kostet Zeit, trotzdem meist sinnvoll Objekt häufiger zu erzeugen und zu schließen/löschen • Nutzung einer Transaktion EntityTransaction tr = em.getTransaction(); em.getTransaction(); – kurzfristig nutzen: Daten vorbereiten, dann DB-Zugriff, dann schließen • letztendlich immer alles schließen (typisch in finally-Block) • Generelles Verhalten hängt von DB ab, in Großprojekten immer mit erfahrenem DB-Administrator arbeiten Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 165 Entity ist POJO Entity-Klassen-Objekte sind klassische Plain Old Java Objects • Verpflichtung: – public (oder protected) Konstruktor ohne Parameter – Exemplarvariablen private oder protected, Zugriff über get... und set... – Klasse, Methoden, Exemplarvariablen nicht final – Serialisierbar [zumindest sehr sinnvoll] • keine weiteren Einschränkungen – beliebige weitere Methoden – Vererbung (auch Entity von Nicht-Entity [aufwändig]) – Nutzung abstrakte Klassen Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 166 Primary Key Typisch: Primary Key wird erzeugt @Id @GeneratedValue(strategy = GenerationType.AUTO) private int minr; folgende Datentypen erlaubt • primitive Java-Typen (int, long, …) • Wrapper von primitiven Java-Typen (Integer, Long, …) • java.lang.String • java.util.Date • java.sql.Date Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 167 Persistierbare Typen/Klassen • Primitive Typen (byte, char, int, long, float, double, boolean) • java.lang.String • Andere serialisierbare Typen: – Wrapper der primitiven Typen – java.math.BigInteger – java.math.BigDecimal – java.util.Date, java.util.Calendar – java.sql.Date, java.sql.Time, java.sql.TimeStamp – Nutzerdefinierte serialisierbare Typen – byte[], Byte[], char[], Character[] • Enumeration • Andere Entities • Collections von Entities (Collection, Set, List, Map) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 168 Persistent Fields / Properties • Persistent Fields – @Id private int minr; – Exemplarvariablen direkt annotiert – Zugriff des Persistence-Frameworks direkt auf Variablen – Vorteil: Variable und Annotation stehen direkt zusammen • Persistent Properties – @Id public int getMinr{ return this.minr; } – get-Methode wird annotiert – Zugriff auf Exemplarvariablen muss immer über Standard get erfolgen (auch in der Klasse selbst) – Vorteil: Flexibilität, da Methode weitere Funktionalität haben kann Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 169 Annotationen zur Flexibilisierung / Ergänzung @Entity @Table(name @Table(name="Chef") name="Chef") public class Mitarbeiter implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue( GeneratedValue(strategy = GenerationType.AUTO) GenerationType.AUTO) private int minr; minr; @Column( Column( name="Leiter", name="Leiter", nullable= nullable=false ,updatable= updatable=true, true, unique=true) unique=true) private String name; name; ... Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 170 PersistenceContext • • • • • Wird für Objekte vorher festgehaltener Klassen definiert Entspricht einem Cache, der MANAGED-Objekte verwaltet EntityManager-Objekt für konkreten PersistenceContext EntityManager-Operationen arbeiten auf dem Cache Man muss EntityManager mitteilen, dass Daten in die DB geschrieben werden müssen em.getTransaction(). em.getTransaction().commit ().commit(); commit(); • Beim Schreiben können wg. der Transaktionssteuerung der DB Exceptions auftreten (abhängig von Steuerungsart) • Im Zweifel bei Entwicklung immer echte Tabellen anschauen • Üblich: nur kurz lebende EntityManager (erzeugen, Aktion, schließen) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 171 Beispiel für Cache (1/2) @Entity public class Mitarbeiter implements Serializable { @Id @GeneratedValue private int minr; @Column(length=2) //maximal zwei Zeichen private String name; public Mitarbeiter() { } //parameterloser Konstruktor benötigt public Mitarbeiter(String name) { this.name = name; } // getget- und setset-Methoden weggelassen @Override public String toString(){ return name+"("+minr+")"; } Komponentenbasierte Software} Entwicklung Prof. Dr. Stephan Kleuker 172 Beispiel für Cache (2/2) public static void main(String[] main(String[] args) args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory(" Persistence.createEntityManagerFactory("JPACachePU ("JPACachePU"); JPACachePU"); EntityManager em= em=emf.createEntityManager(); emf.createEntityManager(); em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.persist( em.persist(new Mitarbeiter("ET")); em.persist( em.persist(new Mitarbeiter("JFK")); for( for(int i=1;i<3;i++) System.out.println( System.out.println(em.find( em.find(Mitarbeiter.class,i)); Mitarbeiter.class,i)); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); em.close(); em.close(); } ET(1) } JFK(2) [EL Warning]: 20102010-0303-28 13:12:57.86813:12:57.868-UnitOfWork(20290587)UnitOfWork(20290587)also Exception [EclipseLink[EclipseLink-4002]: kein org.eclipse.persistence.exceptions.DatabaseException Eintrag Internal Exception: java.sql.SQLDataException: Bei dem in der Versuch, VARCHAR 'JFK' auf die Länge 2 zu kürzen, ist DB ein Abschneidefehler aufgetreten. Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 173 Lebenslauf eines Entity-Objekts NEW -> merge() führt evtl. zur Mehrfachobjekterzeugung refresh() nur, wenn vorher persistiert Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 174 Lebenslaufanalyse (1/16) - Beispielentität @Entity public class Mitarbeiter2 implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue( GeneratedValue(strategy = GenerationType.AUTO) GenerationType.AUTO) private int id; id; // mit getget- und setset-Methode private String name; name; // mit getget- und setset-Methode public Mitarbeiter2(){} Mitarbeiter2(){} public Mitarbeiter2(int Mitarbeiter2(int val, val, String name){ name){ this.id = val; val; this.name = name; name; } @Override public String toString() toString() { return "["+this.id+": "+this.name+"]"; } // ... Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 175 Lebenslaufanalyse (2/16) – Umgebung (nichts Neues) public class Main2 { private EntityManagerFactory emf = Persistence .createEntityManagerFactory("JPASpielereiPU2"); createEntityManagerFactory("JPASpielereiPU2"); private EntityManager em = emf.createEntityManager(); emf.createEntityManager(); // ... persistence.xml: <persistencepersistence-unit name="JPASpielereiPU2" name="JPASpielereiPU2" transactiontransaction-type="RESOURCE_LOCAL type="RESOURCE_LOCAL"> ="RESOURCE_LOCAL"> <provider> provider>org.eclipse.persistence.jpa.PersistenceProvider </provider </provider> provider> <class>jpaspielerei.Mitarbeiter2</ class>jpaspielerei.Mitarbeiter2</class >jpaspielerei.Mitarbeiter2</class> class> <properties> properties> <property name="javax.persistence.jdbc.url" name="javax.persistence.jdbc.url" value=" value="jdbc:derby ="jdbc:derby://localhost:1527/Spielerei"/> jdbc:derby://localhost:1527/Spielerei"/> <property name=" name="javax.persistence.jdbc.password ="javax.persistence.jdbc.password" javax.persistence.jdbc.password" value=" value="kleuker ="kleuker"/> kleuker"/> ... Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 176 Lebenslaufanalyse (3/16) - Analysemethode public void beinhaltet(int beinhaltet(int nr, nr, Mitarbeiter2 ma) ma) { System.out.println( System.out.println(nr + ": „ + ma + ": "+ this.em.contains( this.em.contains(ma)); ma)); Map<String Map<String, <String, Object> Object> map = this.em.getProperties(); this.em.getProperties(); System.out.println(" System.out.println("----------------("-----------------"); -----------------"); try (Connection con = DriverManager .getConnection((String) getConnection((String)map.get ((String)map.get("javax.persistence.jdbc.url"), map.get("javax.persistence.jdbc.url"), (String)map.get String)map.get(" map.get("javax.persistence.jdbc.user ("javax.persistence.jdbc.user"), javax.persistence.jdbc.user"), (String)map.get String)map.get(" map.get("javax.persistence.jdbc.password ("javax.persistence.jdbc.password"))) javax.persistence.jdbc.password"))) { Statement stmt = con.createStatement(); con.createStatement(); ResultSet rs = stmt .executeQuery("SELECT executeQuery("SELECT * FROM Mitarbeiter2"); while (rs.next()) rs.next()) { System.out.print( System.out.print(rs.getString(" rs.getString("id ("id") id") + ": " + rs.getString(" rs.getString("name ("name")+" name")+"\ ")+"\n"); } } catch(Exception catch(Exception e){ System.out.println(" System.out.println("Datenbankfehler ("Datenbankfehler: Datenbankfehler: "+e.getMessage "+e.getMessage()); e.getMessage()); } System.out.println(" System.out.println("----------------("-----------------\ -----------------\n"); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 177 Lebenslaufanalyse (4/16) - Beispielschritte public void lifecycle1() { Mitarbeiter2 m = new Mitarbeiter2(1, "ich"); beinhaltet(0, m); m 0: [1: ich]: false --------------------------------- 1 ich em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.persist(m em.persist(m); (m); beinhaltet(1, beinhaltet(1, m); m); m Komponentenbasierte SoftwareEntwicklung 1 ich Prof. Dr. Stephan Kleuker id name id name id name 1 ich PC 1 ich em.getTransaction(). em.getTransaction().commit ().commit(); commit(); beinhaltet(2, beinhaltet(2, m); m PC PC 178 Lebenslaufanalyse (5/16) - Beispielschritte PC m.setName(" m.setName("Deep ("Deep Thought"); Thought"); beinhaltet(3, beinhaltet(3, m); 1 Deep Thought m em.refresh(m em.refresh(m); (m); beinhaltet(4, beinhaltet(4, m); m.setName(" m.setName("Calculon ("Calculon"); Calculon"); beinhaltet(5, beinhaltet(5, m); name 1 ich id name 1 ich id name 1 ich id name 1 ich PC 1 ich m PC 1 Calculon m Mitarbeiter2 m2 = em.merge(m); em.merge(m); beinhaltet(6, beinhaltet(6, m2); m m2 Komponentenbasierte SoftwareEntwicklung id PC 1 Calculon Prof. Dr. Stephan Kleuker 179 Lebenslaufanalyse (6/16) - Beispielschritte em.persist(m em.persist(m); (m); beinhaltet(7, beinhaltet(7, m); PC 1 Calculon m m2 em.getTransaction(). em.getTransaction().begin ().begin(); begin(); beinhaltet(8, beinhaltet(8, m); m m2 em.getTransaction(). em.getTransaction().commit ().commit(); commit(); beinhaltet(9, beinhaltet(9, m); m); m m2 id name 1 ich id name 1 ich id name 1 Calculon PC 1 Calculon PC 1 Calculon • persist(.) persist(.) gibt nur an, dass ein Objekt gespeichert werden soll, dies ist außerhalb von Transaktionen möglich (man sollte aber einheitlichen Stil nutzen und dies vermeiden) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 180 Lebenslaufanalyse (7/16) - Beispielschritte m.setName(" m.setName("Linguo ("Linguo"); Linguo"); em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); beinhaltet(10, beinhaltet(10, m); m); em.clear(); em.clear(); beinhaltet(11, beinhaltet(11, m); m); m m2 PC 1 Linguo id name 1 Linguo id name 1 Linguo PC m m2 1 Linguo • Sollen in einer Transaktion zwischenzeitlich alle SQL-Befehle ausgeführt, dann weitere Aktionen und dann ein commit ausgeführt werden, wird em.flush() em.flush() genutzt Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 181 Lebenslaufanalyse (8/16) - Beispielschritte m2 = em.find(Mitarbeiter2.class, em.find(Mitarbeiter2.class, 1); System.out.println("m // false System.out.println("m == m2 : "+(m == m2)); m.setName("HAL"); m.setName("HAL"); m2.setName("EVE"); m2.setName("EVE"); beinhaltet(12, beinhaltet(12, m); beinhaltet(13, beinhaltet(13, m2); m2); PC m 1 HAL m2 m = em.merge(m em.merge(m); (m); beinhaltet(14, beinhaltet(14, m); beinhaltet(15, beinhaltet(15, m2); 1 EVE id name 1 Linguo id name 1 Linguo PC m m2 1 HAL System.out.println("m System.out.println("m == m2 :"+(m==m2)); :"+(m==m2)); // true • merge merge(.) (.) aktualisiert/vermischt lokales PC-Objekt mit Daten des übergebenen Objektes und gibt PC-Objekt zurück Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 182 Lebenslaufanalyse (9/16) - Beispielschritte em.remove(m em.remove(m); (m); beinhaltet(16, beinhaltet(16, m); beinhaltet(17, beinhaltet(17, m2); m2); m m2 PC 1 HAL id name 1 Linguo em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.flush(); em.flush(); // nicht weglassbar, warum ??? em.getTransaction(). em.getTransaction().commit ().commit(); commit(); beinhaltet(18, beinhaltet(18, m); // true !!!??? (UPDATE und DELETE) PC m m2 1 HAL em.clear(); em.clear(); beinhaltet(19, beinhaltet(19, m); m m2 name id name PC 1 HAL Komponentenbasierte SoftwareEntwicklung id Prof. Dr. Stephan Kleuker 183 Lebenslaufanalyse (10/16) - Beispielschritte m = em.merge(m); em.merge(m); beinhaltet(20, beinhaltet(20, m); PC m m2 1 HAL em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.flush(); em.flush(); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); beinhaltet(21, beinhaltet(21, m); m); 1 em.remove(m em.remove(m); (m); beinhaltet(22, beinhaltet(22, m); m m2 id name id name 1 HAL id name 1 HAL PC HAL PC m m2 1 HAL Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 184 Lebenslaufanalyse (11/16) - Beispielschritte em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); beinhaltet(23, beinhaltet(23, m); m m2 PC id 1 HAL name em.close(); em.close(); beinhaltet(24, beinhaltet(24, m); m); // Exception in thread "main" java.lang.IllegalStateException: java.lang.IllegalStateException: Attempting to execute an operation on a closed EntityManager. Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 185 Lebenslaufanalyse (12/16) - Fazit • man sollte wissen, welche Objekte sich im Persistence Context (PC) jeweils befinden • man braucht definitiv nicht alle gezeigten Varianten • typischer Ansatz – sehr kurze Transaktionen – persist(.) und remove(.) finden in den Transaktionen statt – merge(.) seltener, aber notwendig, um lokale Änderungen in PC zu übertragen – wenn man aufräumen will clear() explizit nutzen – weitere Befehle nur nutzen, wenn unbedingt notwendig • vom Ansatz abweichen, wenn viele Transaktionen SW ausbremsen (dann wird es kompliziert) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 186 Lebenslaufanalyse (13/16) – Zweites Beispiel public void lifecycle2() { Mitarbeiter2 m = new Mitarbeiter2(1, "ich"); Mitarbeiter2 m2 = new Mitarbeiter2(1, "Nono "Nono"); Nono"); try { em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.persist(m); em.persist(m); beinhaltet(1, m); m); PC m2 1 Nono m 1 ich em.persist(m2 em.persist(m2); (m2); beinhaltet(2, m2); id name id name PC m m2 1 ich 1 Nono em.getTransaction(). em.getTransaction().commit ().commit(); commit(); Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 187 Lebenslaufanalyse (14/16) – Zweites Beispiel } catch (Exception (Exception e) { System.out.println( System.out.println(e.getMessage()); e.getMessage()); if (em.getTransaction(). em.getTransaction().isActive ().isActive()) isActive()) { em.getTransaction(). em.getTransaction().rollback ().rollback(); rollback(); System.out.println(" System.out.println("rolled ("rolled back"); } } // liefert nur Internal Exception: Exception: // java.sql.SQLIntegrityConstraintViolationException: java.sql.SQLIntegrityConstraintViolationException: beinhaltet(3, beinhaltet(3, m); PC m 1 ich m2 id 1 Nono Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker name 188 Lebenslaufanalyse (15/16) – Zweites Beispiel em.persist(m em.persist(m); (m); em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); beinhaltet(4, beinhaltet(4, m); m2 1 Nono PC 1 ich m em.persist(m2 em.persist(m2); (m2); beinhaltet(5, beinhaltet(5, m2); em.refresh(m2 em.refresh(m2); (m2); beinhaltet(6, beinhaltet(6, m2); id name 1 ich id name 1 ich id name 1 ich PC m m2 1 ich 1 Nono PC m m2 1 ich 1 ich em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); // java.sql.SQLIntegrityConstraintViolationException Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 189 Lebenslaufanalyse (16/16) – SQL-Befehle sichtbar für Eclipselink in persistence.xml: <property name=" name="eclipselink.logging.level eclipselink.logging.level" eclipselink.logging.level" value="SEVERE"/> <property name="eclipselink.logging.level name="eclipselink.logging.level" eclipselink.logging.level" value=“FINE"/> <property name="eclipselink.logging.level name="eclipselink.logging.level" eclipselink.logging.level" value=“FINEST"/> allgemein ab JPA 2.1, SQL-Skripte erzeugen <property name=" name="javax.persistence.schema ="javax.persistence.schemajavax.persistence.schema-generation.scripts.action" generation.scripts.action" value=" value="drop ="dropdrop-andand-create"/> create"/> <property name=" name="javax.persistence.schema ="javax.persistence.schemajavax.persistence.schema-generation.scripts.dropgeneration.scripts.drop-target" value=" value="mydrop.ddl ="mydrop.ddl"/> mydrop.ddl"/> <property name=" name="javax.persistence.schema ="javax.persistence.schemajavax.persistence.schema-generation.scripts.creategeneration.scripts.create-target" value=" value="mycreate.ddl ="mycreate.ddl"/> mycreate.ddl"/> Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 190 Sauberes Persistieren • Auslagerung der Persistierung in eine Methode mit sauberer Exception-Behandlung • Anmerkung: man muss nicht immer em.close() machen public void persist(Object persist(Object object) object) { em.getTransaction(). em.getTransaction().begin ().begin(); begin(); try { em.persist( em.persist(object); object); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); } catch (Exception e) { if (em.getTransaction(). em.getTransaction().isActive ().isActive()) isActive()) em.getTransaction(). em.getTransaction().rollback ().rollback(); rollback(); throw e; // oder neue Exception } finally { em.close(); em.close(); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 191 Klasse oder Tabelle? Bei der Entity-Nutzung offen, ob erst Klassen designt und dann Tabellen entworfen werden • Einfach: Tabellen existieren; dann typischerweise zur Tabelle eine Entity-Klassse erstellbar (generierbar) • Wenn nichts gegeben: – Entwurf der Entity-Klassen (Daten der Applikation mit ihren Abhängigkeiten) – Ableitung oder Generierung der Tabellen • Generierungsansätze: – Drop and Create: beteiligte Tabellen löschen und neu anlegen (Entwicklung und Test) – Create: wenn nicht existent, dann anlegen (Realität) – None: wenn nicht existent, dann Fehler (Realität) • Hinweis: bei Änderungen neu übersetzen Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 192 Generelle JEE-Regel Convention over Configuration • bedeutet: wenn nichts angegeben wird, wird ein DefaultWert genutzt • Default-Werte sind zwar sinnvoll, sollte man aber kennen • Erinnerung: Java-Inkonsistenz Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 193 Einschub: XML-Konfiguration • Statt Annotationen zu nutzen, können diese Informationen auch in XML beschrieben werden • Typisch: eine XML-Datei pro Klasse + zusammenführende XML-Datei • Vorteil: Verhaltensänderungen ohne Codeänderung • Nachteil: viele kleine penibel zu pflegende Dateien • Auch möglich: XML und Annotationen; dabei „schlägt“ XML die Annotationen Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 194 Kardinalitäten in JPA A x y B • 1:1 zu einem A- Objekt gehört (maximal) ein anderes BObjekt, die jeweils zu (maximal) einem A-Objekt gehören • 1:N zu einem A-Objekt gehören beliebig viele B-Objekte, die jeweils zu (maximal) einem A-Objekt gehören (N:1 analog) • M:N zu einem A-Objekt gehören beliebig viele B-Objekte, die jeweils zu beliebig vielen A-Objekten gehören • Anders als bei Tabellen haben OO-Assoziationen Leserichtungen • Unidirektional: nur von einer Seite auf die andere schließbar • Bidirektional: Abhängigkeit in beide Richtungen manövrierbar (es gibt Besitzer der Beziehung; für Updates wichtig) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 195 Umsetzung unidirektional 1:N (1/4) @Entity public class Projektauftrag implements Serializable { @Id @GeneratedValue private int paid; private String titel; public String getTitel() { return titel; } public void setTitel(String titel) { this.titel = titel; } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 196 Umsetzung unidirektional 1:N (2/4) @Entity public class Projekt implements Serializable { @Id @GeneratedValue private int projektid; projektid; private String name; name; @OneToMany( OneToMany(cascade= cascade=CascadeType.PERSIST) CascadeType.PERSIST) private Set<Projektauftrag> auftraege; auftraege; public Projekt(){ auftraege = new HashSet<Projektauftrag>(); HashSet<Projektauftrag>(); } // fehlen getget- und setset-Methoden (auch add wäre sinnvoll) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 197 Umsetzung unidirektional 1:N (3/4) public void datenAnlegen(){ String p[] ={"Hotel", "Noten", "Belegung"}; String a[] ={"Analyse", "Modell", "Design"}; em.getTransaction().begin(); for (int i=0; i<p.length;i++){ Projekt pr= new Projekt(); pr.setName(p[i]); for (int j=0; j<a.length;j++){ Projektauftrag pa= new Projektauftrag(); pa.setTitel(a[j]); Set<Projektauftrag> tmp= pr.getAuftraege(); tmp.add(pa); } em.persist(pr); } em.getTransaction().commit(); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 198 Umsetzung unidirektional 1:N (4/4) SELECT PROJEKT.PROJEKTID, PROJEKT.NAME, PROJEKTAUFTRAG.PAID, PROJEKTAUFTRAG.TITEL FROM PROJEKT, PROJEKT_PROJEKTAUFTRAG, PROJEKTAUFTRAG WHERE PROJEKT.PROJEKTID = PROJEKT_PROJEKTAUFTRAG.PROJEKT_PROJEKTID AND PROJEKTAUFTRAG.PAID = PROJEKT_PROJEKTAUFTRAG.AUFTRAEGE_PAID; Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 199 Cascade-Varianten @OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE}) • MERGE : merge() implizit für verknüpfte Objekte aufrufen • PERSIST: persist() implizit für verknüpfte Objekte aufrufen • REFRESH: refresh() implizit für verknüpfte Objekte aufrufen • REMOVE: remove() implizit für verknüpfte Objekte aufrufen • ALL: alle vier genannten Möglichkeiten • Default-Einstellung: keine der fünf Varianten • Wichtige Design-Entscheidung, was sinnvoll ist • REMOVE nur bei @OneToOne und @OneToMany nutzbar Beispiel, wenn Cascade.PERSIST fehlte SCHWERWIEGEND: Could not synchronize database state with session org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Projektauftrag Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 200 Umsetzung bidirektional 1:N (1/5) - Bidirektionale Relationen haben Eigentümer (owner side) und Abhängigen (inverse side) - Eigentümer bei 1:N ist N (hier Projektauftrag) - Abhängiger bekommt mappedBy-Attribut - Programmierer für Einträge auf beiden Seiten verantwortlich Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 201 Umsetzung bidirektional 1:N (2/5) @Entity public class Projektauftrag implements Serializable { @Id @GeneratedValue private int paid; paid; private String titel; titel; @ManyToOne( ManyToOne(cascade={ cascade={ CascadeType.PERSIST ,CascadeType.MERGE}) CascadeType.MERGE}) private Mitarbeiter bearbeiter; bearbeiter; //... Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 202 Umsetzung bidirektional 1:N (3/5) @Entity public class Mitarbeiter implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue( GeneratedValue(strategy = GenerationType.AUTO) GenerationType.AUTO) private int minr; minr; private String name; name; @OneToMany( OneToMany(mappedBy=" mappedBy="bearbeiter ="bearbeiter", bearbeiter", cascade={ cascade={CascadeType.PERSIST ={CascadeType.PERSIST, CascadeType.PERSIST, CascadeType.MERGE}) CascadeType.MERGE}) private Set<Projektauftrag> auftraege = new HashSet<Projektauftrag>(); HashSet<Projektauftrag>(); public void auftragHinzu(Projektauftrag auftragHinzu(Projektauftrag pa){ pa){ auftraege.add( auftraege.add(pa); pa); } // ... Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 203 Umsetzung bidirektional 1:N (4/5) public void mitarbeiterZuordnen(){ mitarbeiterZuordnen(){ Mitarbeiter m[]= {new {new Mitarbeiter("Egon"), new Mitarbeiter(" Mitarbeiter("Aise ("Aise"), Aise"), new Mitarbeiter("Urs")}; em.getTransaction(). em.getTransaction().begin ().begin(); begin(); for (int i=0; i<m.length i<m.length; m.length; i++) em.persist(m[i]); em.persist(m[i]); for (Projektauftrag p : (List<Projektauftrag>) em .createQuery("SELECT createQuery("SELECT p FROM Projektauftrag p") p") .getResultList()) getResultList()) if( if(p.getTitel(). p.getTitel().equals ().equals("Analyse")){ equals("Analyse")){ JPA 1.0m[0].auftragHinzu m[0].auftragHinzu(p); auftragHinzu(p); Variante der p.setBearbeiter(m[0]); p.setBearbeiter(m[0]); Anfrage } else{ else{ m[1].auftragHinzu m[1].auftragHinzu(p); auftragHinzu(p); p.setBearbeiter(m[1]); p.setBearbeiter(m[1]); } em.getTransaction(). em.getTransaction().commit ().commit(); commit(); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 204 Umsetzung bidirektional 1:N (5/5) SELECT MITARBEITER.NAME, MITARBEITER.MINR, PROJEKTAUFTRAG.TITEL, PROJEKTAUFTRAG.PAID FROM MITARBEITER LEFT JOIN PROJEKTAUFTRAG ON (MITARBEITER.MINR=PROJEKTAUFTRAG.BEARBEITER_MINR) ; Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 205 Umsetzung ist OR-Mapper freigestellt Man sieht, dass bei bidirektionalen Beziehungen in EclipseLink keine neuen Tabellen angelegt werden, bei unidirektionalen schon Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 206 Wichtige Annotationen in Großprojekten @Version int version • Attribut wird für das optimistische Locking genutzt; erst beim Schreiben geschaut, ob sich Versionsnummer geändert hat • performant, wenn Objekte nicht häufig geändert werden • Einfach als zusätzliches Attribut ergänzen @Basic(fetch @Basic(fetch= fetch=FetchType.LAZY) FetchType.LAZY) private Set<Projektauftrag> auftraege • EAGER: Alle Daten des Attributs werden bei Objektnutzung sofort in Hauptspeicher geladen (default) • LAZY: Daten werden erst geladen, wenn benötigt • Längere Listen oder komplexe Daten möglichst immer LAZY (versteckte Konsistenzprobleme möglich) • Wenn eine Info sofort benötigt, ist Kette zur Info EAGER Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 207 Umsetzung bidirektional M:N (1/6) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 208 Umsetzung bidirektional M:N (2/6) @Entity public class Projektauftrag implements Serializable { @Id @GeneratedValue private int paid; paid; private String titel; titel; @ManyToOne( ManyToOne(cascade={ cascade={ CascadeType.PERSIST ,CascadeType.MERGE}) CascadeType.MERGE}) private Mitarbeiter bearbeiter; bearbeiter; @ManyToOne( ManyToOne(cascade={ cascade={ CascadeType.PERSIST ,CascadeType.MERGE}) CascadeType.MERGE}) private Rolle rolle; rolle; @Version private int version; version; Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 209 Umsetzung bidirektional M:N (3/6) @Entity public class Rolle implements Serializable { @ID @GeneratedValue @GeneratedValue private int rid; rid; private String name; name; private int tagessatz; tagessatz; @ManyToMany( ManyToMany(cascade = { CascadeType.PERSIST ,CascadeType.MERGE}) CascadeType.MERGE}) @Basic(fetch @Basic(fetch = FetchType.LAZY) FetchType.LAZY) private List<Mitarbeiter> mitarbeiter; mitarbeiter; @Version private int version; version; public void mitarbeiterHinzu(Mitarbeiter mitarbeiterHinzu(Mitarbeiter m) { mitarbeiter.add(m); mitarbeiter.add(m); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 210 Umsetzung bidirektional M:N (4/6) @Entity public class Mitarbeiter implements Serializable { @Id @GeneratedValue( GeneratedValue(strategy = GenerationType.AUTO) GenerationType.AUTO) private int minr; minr; private String name; name; OneToMany( "bearbeiter bearbeiter" @OneToMany (mappedBy = " bearbeiter " ,cascade = {CascadeType.PERSIST {CascadeType.PERSIST, CascadeType.PERSIST, CascadeType.MERGE}) CascadeType.MERGE}) @Basic(fetch @Basic(fetch = FetchType.LAZY) FetchType.LAZY) private Set<Projektauftrag> auftraege; auftraege; ManyToMany( "mitarbeiter mitarbeiter" @ManyToMany (mappedBy = " mitarbeiter " ,cascade = {CascadeType.PERSIST {CascadeType.PERSIST, CascadeType.PERSIST, CascadeType.MERGE}) CascadeType.MERGE}) @Basic(fetch @Basic(fetch = FetchType.LAZY) FetchType.LAZY) private Set<Rolle> rollen = new HashSet<Rolle HashSet<Rolle>(); <Rolle>(); version; @Version private int version ; public void rolleHinzu(Rolle rolleHinzu(Rolle r) { rollen.add(r rollen.add(r); (r); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 211 Umsetzung bidirektional M:N (5/6) public void mitarbeiterZuordnen() mitarbeiterZuordnen() { Mitarbeiter m[] = {new {new Mitarbeiter("Egon") Mitarbeiter("Egon") ,new Mitarbeiter(" Mitarbeiter("Aise ("Aise"), Aise"), new Mitarbeiter("Urs")}; em.getTransaction(). em.getTransaction().begin ().begin(); begin(); for (int i = 0; i < m.length; m.length; i++) em.persist(m[i]); em.persist(m[i]); Rolle r[] = { new Rolle("OOAD",80), new Rolle("SVN",60) Rolle("SVN",60) ,new Rolle("QS",100)}; for (int i = 0; i < r.length; r.length; i++) { for( for(int j = 0; j<m.length j<m.length; m.length; j++) j++) if(r[i]. if(r[i].getName (r[i].getName(). getName().length ().length()<m[j]. length()<m[j].getName ()<m[j].getName(). getName().length ().length()){ length()){ r[i].mitarbeiterHinzu r[i].mitarbeiterHinzu(m[j]); mitarbeiterHinzu(m[j]); m[j].rolleHinzu m[j].rolleHinzu(r[i]); rolleHinzu(r[i]); } em.persist(r[i]); em.persist(r[i]); } // Rollenzuordnung wie bei 1:N mitarbeiterZuordnen() mitarbeiterZuordnen() em.getTransaction(). em.getTransaction().commit ().commit(); commit(); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 212 Umsetzung bidirektional M:N (6/6) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 213 Tabellenstruktur Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 214 Ungeordnete Daten (1/4) @Entity public class Punkt implements Serializable{ @Id @GeneratedValue private int id; private int x; private int y; @Version private int version; public Punkt(int x, int y) { this.x = x; this.y = y; } Problem: Auch Listen werden ungeordnet gespeichert public Punkt(){} @Override public String toString(){ return "["+x+","+y+"]"; } // getget- und setset-Methoden für Exemplarvariablen Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 215 Ungeordnete Daten (2/4) @Entity public class Polygon implements Serializable{ Serializable{ @Id @GeneratedValue private int id; id; @OneToMany( OneToMany(cascade = {CascadeType.ALL {CascadeType.ALL}) CascadeType.ALL}) private List<Punkt> punkte= new ArrayList<Punkt>(); ArrayList<Punkt>(); @Version private int version; version; //get public Polygon(){} // get und set für Exemplarvariablen public void punkteHinzu(Punkt... punkteHinzu(Punkt... pkte){ pkte){ for(Punkt for(Punkt p:pkte) punkte.add(p); punkte.add(p); } @Override public String toString(){ toString(){ StringBuffer erg= erg=new StringBuffer("<"); StringBuffer("<"); for(Punkt for(Punkt p:punkte) erg.append( erg.append(p.toString()); p.toString()); return erg.append(">"). erg.append(">").toString (">").toString(); toString(); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 216 Ungeordnete Daten (3/4) public class Main { private EntityManagerFactory emf = Persistence.createEntityManagerFactory("JPA20NeueFeaturesPU"); Persistence.createEntityManagerFactory("JPA20NeueFeaturesPU"); private EntityManager em = emf.createEntityManager(); emf.createEntityManager(); objekteErzeugen(){ public void objekteErzeugen (){ Punkt[] pkt={ pkt={new ={new Punkt(0,0), new Punkt(5,3), new Punkt(3,3), new Punkt(3,0)}; em.getTransaction(). em.getTransaction().begin ().begin(); begin(); for(Punkt for(Punkt p:pkt) em.persist(p); em.persist(p); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); Polygon p1 = new Polygon(); p1.punkteHinzu(pkt p1.punkteHinzu(pkt[0], pkt[0],pkt [0],pkt[1], pkt[1],pkt [1],pkt[2]); pkt[2]); Polygon p2 = new Polygon(); p2.punkteHinzu(pkt p2.punkteHinzu(pkt[3], pkt[3],pkt [3],pkt[2], pkt[2],pkt [2],pkt[1]); pkt[1]); em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.persist(p1); em.persist(p1); em.persist(p2 em.persist(p2); (p2); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); } Komponentenbasierte SoftwareProf. Dr. 217 Entwicklung Stephan Kleuker Ungeordnete Daten (4/4) public void zeigePolygone(){ zeigePolygone(){ List<Polygon> pl = em.createQuery( em.createQuery( "SELECT p FROM Polygon p",Polygon.class p",Polygon.class). Polygon.class).getResultList ).getResultList(); getResultList(); for(Polygon for(Polygon po:pl) po:pl) System.out.println( System.out.println(po); po); } public void schliessen() schliessen() { if (em != null && em.isOpen()) em.isOpen()) em.close(); em.close(); if (emf != null && emf.isOpen()) emf.isOpen()) emf.close(); emf.close(); } public static void main(String[] args) ) { main(String[] args Main m= new Main(); m.objekteErzeugen(); m.objekteErzeugen(); <[0,0][5,3][3,3]> m.zeigePolygone(); m.zeigePolygone(); <[3,0][3,3][5,3]> m.schliessen(); m.schliessen(); ---System.out.println(" System.out.println("---("----"); ----"); <[0,0][5,3][3,3]> m= new Main(); <[5,3][3,3][3,0]> m.zeigePolygone(); m.zeigePolygone(); m.schliessen(); m.schliessen(); Komponentenbasierte SoftwareProf. Dr. 218 } Entwicklung Stephan Kleuker } Ordnung beibehalten @Entity public class Polygon implements Serializable{ Serializable{ @Id @GeneratedValue private int id; id; @OneToMany( OneToMany(cascade = {CascadeType.PERSIST {CascadeType.PERSIST, CascadeType.PERSIST, CascadeType.MERGE}) CascadeType.MERGE}) @OrderColumn( OrderColumn(name=" name="Ord ="Ord") Ord") private List<Punkt> punkte= new ArrayList<Punkt>(); ArrayList<Punkt>(); @Version private int version; version; <[3,0][3,3][5,3]> <[0,0][5,3][3,3]> ---<[3,0][3,3][5,3]> <[0,0][5,3][3,3]> Hinweis: persistence.xml mit create Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 219 Standard: keine Löschfortsetzung (1/2) public void objekteErzeugen() objekteErzeugen() { Punkt[] pkt = {new {new Punkt(0, 0), new Punkt(5, 3), new Punkt(3, 3)}; Polygon p1 = new Polygon(); p1.punkteHinzu(pkt p1.punkteHinzu(pkt[0], pkt[0], pkt[1], pkt[1], pkt[2]); pkt[2]); em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.persist(p1); em.persist(p1); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); } public void objekteBearbeiten() objekteBearbeiten() { Polygon pl = em.createQuery("SELECT em.createQuery("SELECT p FROM Polygon p", Polygon.class). Polygon.class).getResultList ).getResultList(). getResultList().get ().get(0); get(0); pl.getPunkte(). pl.getPunkte().remove ().remove(1); remove(1); em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.persist( em.persist(pl); pl); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 220 Standard: keine Löschfortsetzung (2/2) public void zeigePolygoneUndPunkte() zeigePolygoneUndPunkte() { for (Polygon po : em.createQuery("SELECT em.createQuery("SELECT p FROM Polygon p", Polygon.class). Polygon.class).getResultList ).getResultList()) getResultList()) System.out.println( System.out.println(po); po); System.out.println(" System.out.println("---("----"); ----"); for (Punkt pu : em.createQuery("SELECT em.createQuery("SELECT p FROM Punkt p", Punkt.class). Punkt.class).getResultList ).getResultList()) getResultList()) System.out.println( System.out.println(pu); pu); } public static void main(String[] main(String[] args) args) { Main m = new Main(); m.objekteErzeugen(); m.objekteErzeugen(); m.objekteBearbeiten(); m.objekteBearbeiten(); m.zeigePolygoneUndPunkte(); m.zeigePolygoneUndPunkte(); m.schliessen(); m.schliessen(); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker <[0,0][3,3]> ---[0,0] [5,3] [3,3] 221 Löschfortsetzung • Anmerkung: auch keine Löschung alleine durch CASCADETYPE.ALL in Polygon, aber durch folgende Ergänzung @OneToMany( {CascadeType.ALL CascadeType.ALL}, OneToMany(cascade = { CascadeType.ALL}, orphanRemoval= orphanRemoval=true) true) @OrderColumn( OrderColumn(name=" name="Ord ="Ord") Ord") private List<Punkt> punkte = new ArrayList<Punkt>(); ArrayList<Punkt>(); <[0,0][3,3]> ---[0,0] [3,3] • Was passiert, wenn mehrere Objekte Punkt referenzieren (widerspricht der Eigentümerschaft)? Exception in thread "main“ javax.persistence.RollbackException Caused by: by: java.sql.SQLIntegrityConstraintViolationException: java.sql.SQLIntegrityConstraintViolationException: DELETE in Tabelle 'PUNKT' hat für Schlüssel (3) die Integritätsbedingung 'PLYGONPUNKTPNKTEID' für Fremdschlüssel verletzt. Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 222 Beispieldaten (in DB-Notation) Projekt Projektauftrag Rolle Projekt_Projektauftrag Mitarbeiter Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 223 Anfragen • Anfragesprache soll möglichst Entity-Objekte liefern • Anfragesprache soll DB-unabhängig sein (SQL-Detailproblem) • Antwort: Java Persistence QL (JPQL) – Ermöglicht direkte Zurückgabe von Entitätsobjektlisten – Ermöglicht auch direkte Ausführung von SQL-Anfragen • Anmerkung: Vorgänger JDO unterstützte OO-Features in JDOQL (Methodennutzung); dies ist nicht mehr möglich • Typische Struktur: – SELECT p FROM Projekt p WHERE <Bed> – Übersetzung: Wähle aus der Menge Projekt der gemanageten Objekte die Elemente p mit Eigenschaft <Bed> Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 224 Anfrageausführung • Setzt ordentliches toString() voraus • ist nicht typsicher public void anfragen(String ql) ql) { try { Query query = em.createQuery( em.createQuery(ql); ql); Collection erg = query.getResultList(); query.getResultList(); for (Iterator it = erg.iterator(); erg.iterator(); it.hasNext();) it.hasNext();) { System.out.println( System.out.println(it.next()); it.next()); } } catch (Exception e) { System.out.println("Anfrage System.out.println("Anfrage gescheitert: " + e.getMessage()); e.getMessage()); } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 225 Einfache Beispiele Vorbemerkung: In FROM-Zeilen stehen Klassen und Attributnamen; bei diesen muss Groß- und Kleinschreibung beachtet werden! SELECT p FROM Projekt p • Projekt Bonitaet (5) [Konten Historie Raten ] • Projekt Bremse (11) [Display Sensoren Fusion ] Direkter Zugriff auf Attribute SELECT p.name FROM Projekt p • Bonitaet • Bremse Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 226 Nutzung mehrwertiger Relationen (1/2) SELECT p.auftraege FROM Projekt p • Aufgabe Raten (10) durch Ivan als Java • Aufgabe Konten (6) durch Fatma als Cobol • Aufgabe Historie (8) durch Urs als Java • Aufgabe Sensoren (12) durch Ivan als C • Aufgabe Display (14) durch Fatma als Java • Aufgabe Fusion (15) durch Ivan als C Nicht erlaubt: SELECT p.auftraege.bearbeiter FROM Projekt p • An exception occurred while creating a query in EntityManager: EntityManager: Exception Description: Error compiling the query [SELECT p.auftraege.bearbeiter FROM Projekt p], line 1, column 9: invalid navigation expression [p.auftraege.bearbeiter [p.auftraege.bearbeiter], p.auftraege.bearbeiter], cannot navigate collection valued association field [auftraege [auftraege]. auftraege]. Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 227 Nutzung mehrwertiger Relationen (2/2) SELECT pa.bearbeiter FROM Projektauftrag pa 1: Ivan Auftraege=[ Auftraege=[ Sensoren Fusion Raten ] Rollen=[ C Java ] 1: Ivan Auftraege=[ Auftraege=[ Sensoren Fusion Raten ] Rollen=[ C Java ] 1: Ivan Auftraege=[ Auftraege=[ Sensoren Fusion Raten ] Rollen=[ C Java ] 2: Fatma Auftraege=[ Auftraege=[ Konten Display ] Rollen=[ Cobol Java ] 2: Fatma Auftraege=[ Auftraege=[ Konten Display ] Rollen=[ Cobol Java ] 3: Urs Auftraege=[ Auftraege=[ Historie ] Rollen=[ Java ] Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 228 Neu zusammengesetzte Ergebnisse • Folgende Folie zeigt woher Details der Ausgabe kommen SELECT r.name, r.tagessatz FROM Rolle r • [Ljava.lang.Object;@1db5ec Ljava.lang.Object;@1db5ec :: [Ljava.lang.Object [Ljava.lang.Object; Ljava.lang.Object; C 50 • [Ljava.lang.Object;@92b1a1 Ljava.lang.Object;@92b1a1 :: [Ljava.lang.Object [Ljava.lang.Object; Ljava.lang.Object; Java 60 • [Ljava.lang.Object;@cbf9bd Ljava.lang.Object;@cbf9bd :: [Ljava.lang.Object [Ljava.lang.Object; Ljava.lang.Object; Cobol 70 Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 229 Ausgabe mit detaillierterer Analyse public void anfragen2(String ql) { System.out.println(ql); try { Query query = em.createQuery(ql); Collection erg = query.getResultList(); for (Iterator it = erg.iterator(); it.hasNext();) { Object o = it.next(); System.out.println(o+" :: "+ o.getClass().getName()); if(o.getClass().getName().equals("[Ljava.lang.Object;")){ Object oa[]= (Object[]) o; for(int i=0;i<oa.length;i++) System.out.println(" "+oa[i]); } } } catch (Exception e) { System.out.println("Anfragefehler: " + e.getMessage()); } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 230 JOIN-Varianten SELECT m.name FROM Projektauftrag pa, pa, Mitarbeiter m WHERE pa.bearbeiter=m pa.bearbeiter=m AND pa.titel='Sensoren' pa.titel='Sensoren' SELECT m.name FROM Projektauftrag pa JOIN pa.bearbeiter m WHERE pa.titel='Sensoren' pa.titel='Sensoren' • Ivan SELECT DISTINCT(pa.bearbeiter.name) FROM Projekt p JOIN p.auftraege pa WHERE p.name='Bremse' • Fatma • Ivan Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 231 Vordefinierte Anfragen + typisches Problem private static final String PROJEKT_VON= "SELECT DISTINCT(p.name) " +"FROM Projekt p JOIN p.auftraege pa " +"WHERE pa.bearbeiter.name= :mname :mname"; mname"; public void projektVon(String projektVon(String name){ name){ Query query = em.createQuery(PROJEKT_VON em.createQuery(PROJEKT_VON) (PROJEKT_VON) .setParameter(" setParameter("mname ("mname", mname", name); name); Collection erg = query.getResultList(); query.getResultList(); for (Iterator it = erg.iterator(); erg.iterator(); it.hasNext();) it.hasNext();) { System.out.println( System.out.println(it.next()); it.next()); } } projektVon(" projektVon("Urs ("Urs' Urs' OR NOT(p.name='bla NOT(p.name='bla') bla') OR p.name='bla p.name='bla") bla") • Bremse • Bonitaet Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 232 echte Standardkonformität? • • • • gerade bei Anfragen häufig keine 100%-Kompatibilität Hibernate meist etwas mächtiger als EclipseLink Beispiel: Zeige alle C-Programmierer geht: SELECT m FROM Rolle r JOIN r.mitarbeiter m WHERE r.name='C‘ [1: Ivan... • ging lange Zeit nicht in EclipseLink: SELECT m FROM Mitarbeiter m JOIN m.rollen r WHERE r.name='C' invalid navigation expression [r.name], cannot navigate expression [r] of type [java.util.List [java.util.List] java.util.List] inside a query. • trotzdem: Immer sinnvoll Objektauswahlen in Anfragesprache durchzuführen, nicht alle Objekte aus DB lutschen und dann verarbeiten Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 233 Klassische SQL-Operatoren SELECT p.name, COUNT(pa.titel COUNT(pa.titel) pa.titel) FROM Projekt p JOIN p.auftraege pa GROUP BY p.name • [Ljava.lang.Object;@4a9a7d Ljava.lang.Object;@4a9a7d :: [Ljava.lang.Object [Ljava.lang.Object; Ljava.lang.Object; Bonitaet 3 • [Ljava.lang.Object;@1e4a47e Ljava.lang.Object;@1e4a47e :: [Ljava.lang.Object [Ljava.lang.Object; Ljava.lang.Object; Bremse 3 • Erinnerung AVG, SUM, MIN, MAX Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 234 Named Queries @NamedQueries({ NamedQueries({ //in Mitarbeiter (@NamedQuery (@NamedQuery( NamedQuery(name=" name="Mitarbeiter.primaryKey ="Mitarbeiter.primaryKey" Mitarbeiter.primaryKey" ,query="SELECT query="SELECT m FROM Mitarbeiter m WHERE m.minr= m.minr= :minr :minr"), minr"), @NamedQuery( NamedQuery(name=" name="Mitarbeiter.mitFaehigkeit ="Mitarbeiter.mitFaehigkeit" Mitarbeiter.mitFaehigkeit" ,query="SELECT query="SELECT m FROM Rolle r JOIN r.mitarbeiter m" +" WHERE r.name = :name :name") name") }) // z. B. in Main mit lokalen Variablen minr und faehigkeit Mitarbeiter m= em .createNamedQuery( createNamedQuery( "Mitarbeiter.primaryKey "Mitarbeiter.primaryKey" Mitarbeiter.primaryKey" ,Mitarbeiter.class) Mitarbeiter.class) .setParameter(" setParameter("minr ("minr", minr", minr) minr) .getSingleResult(); getSingleResult(); for( for( Mitarbeiter m:em // EclipseLink Object m:em .createNamedQuery( createNamedQuery( "Mitarbeiter.mitFaehigkeit "Mitarbeiter.mitFaehigkeit" Mitarbeiter.mitFaehigkeit" ,Mitarbeiter.class) Mitarbeiter.class) .setParameter(" setParameter("name ("name", name", faehigkeit) faehigkeit) .getResultList()) getResultList()) System.out.println(m); System.out.println(m); Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 235 Flexiblere Anfragenkonstruktion (Ausblick) public void ooartigeAnfragekonstruktion(){ CriteriaBuilder qB = em.getCriteriaBuilder(); CriteriaQuery<Mitarbeiter> cQ = qB.createQuery(Mitarbeiter.class); Root<Mitarbeiter> mAlias= cQ.from(Mitarbeiter.class); cQ.where(qB.notEqual(mAlias.get("name"), "Urs")); TypedQuery<Mitarbeiter> tq=em.createQuery(cQ); for(Mitarbeiter m:tq.getResultList()) System.out.println(m.getName()); } Ivan Fatma Heinz Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 236 Vererbung – eine Tabelle (1/3) @Entity public class Produkt implements Serializable { @Id @GeneratedValue private int prnr; private String name; private int lagermenge; private float preis; @Version private int version; ...} @Entity public class Lebensmittel extends Produkt implements Serializable{ @Temporal(javax.persistence.TemporalType.DATE) private Date verfallsdatum; ...} @Entity public class Buch extends Produkt{ Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker ...} 237 Vererbung – eine Tabelle (2/3) public static void main(String[] main(String[] args) args) { EntityManagerFactory eMF =Persistence. Persistence. createEntityManagerFactory(" createEntityManagerFactory("JPAVererbungPU ("JPAVererbungPU"); JPAVererbungPU"); EntityManager em= em=eMF.createEntityManager(); eMF.createEntityManager(); em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.persist( em.persist(new Buch("JPA", 2, 39.99f)); em.persist( em.persist(new Produkt("Maus", 4, 7.99f)); em.persist( em.persist(new Lebensmittel("Tofu", 7, 0.69f,new Date())); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); for(Produkt for(Produkt p:(List<Produkt>)em p:(List<Produkt>)em .createQuery("SELECT createQuery("SELECT p FROM Produkt p").getResultList p").getResultList()) getResultList()) System.out.println(p System.out.println(p); (p); } 1:JPA Menge:2 Preis:39.99 Buch 2:Maus Menge:4 Preis:7.99 3:Tofu Menge:7 Preis:0.69 Verfall:Thu Oct 15 16:22:14 CEST 2009 Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 238 Vererbung – eine Tabelle (3/3) SELECT * FROM Produkt • Abbildung in eine Tabelle ist Default-Einstellung • Ansatz meist am performantesten • (float ungeeignet für Geldbeträge) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 239 Vererbung – getrennte verknüpfte Tabellen @Entity @Inheritance( Inheritance(strategy= strategy=InheritanceType.JOINED) InheritanceType.JOINED) public class Produkt implements Serializable { ...} SELECT * FROM Produkt SELECT * FROM Lebensmittel SELECT * FROM Buch Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 240 Vererbung – getrennte Tabellen @Entity @Inheritance( Inheritance(strategy= strategy=InheritanceType.TABLE_PER_CLASS) InheritanceType.TABLE_PER_CLASS) public class Produkt implements Serializable { ...} SELECT * FROM Produkt SELECT * FROM Lebensmittel SELECT * FROM Buch Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 241 Software-Architektur • sinnvoll: Datenzugriff kapseln • typisch mehrere DAO-Klassen • entsteht Datenzugriffskomponente Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 242 Einbindung von Bean-Validation • Annotationen wie @Column ermöglichen bereits Angabe bestimmter Randbedingungen • klarerer Ansatz: Trennung von Beschreibung des Objektgraphen (wer mit wem) von Validierung • Bean-Validation kann zusammen mit JPA genutzt werden; Anwesenheit von Validatoren wird von EntityManagern genutzt • Ansatz: Wenn Daten in DB persistiert werden sollen, werden alle Validierungsregeln geprüft (nicht eher); bei Fehler wird Exception geworfen • Zukunft: Standards werden noch enger verknüpft • Beispiel: externe Programmierernamen beginnen mit „X“, müssen eine der vorgegebenen Sprachen können Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 243 Beispiel: JPA mit Bean-Validation (1/5) @Entity @MrXProgrammer(sprachen={" MrXProgrammer(sprachen={"Java","E (sprachen={"Java","E"}, Java","E"},message "},message="aktuelle message="aktuelle Sprachen") public class Mitarbeiter implements Serializable { @Id @GeneratedValue( GeneratedValue(strategy = GenerationType.AUTO) GenerationType.AUTO) private int minr; minr; @NotNull @Size(min=2, message="echter message="echter Nachname") private String name; name; @OneToMany( OneToMany( mappedBy = "bearbeiter "bearbeiter" bearbeiter" ,cascade = {CascadeType.PERSIST {CascadeType.PERSIST, CascadeType.PERSIST, CascadeType.MERGE}) CascadeType.MERGE}) @Basic(fetch @Basic(fetch = FetchType.EAGER) FetchType.EAGER) @Size(max @Size(max=3, max=3, message=" message="max ="max 3 Auftraege") Auftraege") private Set<Projektauftrag> auftraege = new HashSet<>(); HashSet<>(); @ManyToMany( ManyToMany( mappedBy = "mitarbeiter "mitarbeiter" mitarbeiter" ,cascade = {CascadeType.PERSIST {CascadeType.PERSIST, CascadeType.PERSIST, CascadeType.MERGE}) CascadeType.MERGE}) @Basic(fetch @Basic(fetch = FetchType.EAGER) FetchType.EAGER) @Size(max @Size(max=2, max=2, message=" message="max ="max 2 Rollen") private List<Rolle> rollen = new ArrayList<>(); ArrayList<>(); // ... Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 244 Beispiel: JPA mit Bean-Validation (2/5) @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = MrXValidator.class) @Documented public @interface MrXProgrammer { String message() default "Moduleintrag kaputt"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String[] sprachen() default {}; } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 245 Beispiel: JPA mit Bean-Validation (3/5) public class MrXValidator implements ConstraintValidator<MrXProgrammer, Mitarbeiter> { private String[] muss; public void initialize(MrXProgrammer a) { muss = a.sprachen(); } public boolean isValid(Mitarbeiter t, ConstraintValidatorContext cvc) { System.out.println("Pruefe fuer "+t.getName()); if(!t.getName().startsWith("X")) return true; List<Rolle> rollen = t.getRollen(); for (Rolle r : rollen) for (String s : muss) if (r.getName().equals(s)) return true; return false; } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 246 Beispiel: JPA mit Bean-Validation (4/5) public void beispieldaten(){ beispieldaten(){ Mitarbeiter m1 = new Mitarbeiter("XUrs Mitarbeiter("XUrs"); XUrs"); Rolle[] r= { new Rolle("Java", 60),new 60),new Rolle("C", 50)}; for( for(int i=0;i<r.length;i i=0;i<r.length;i++) r.length;i++) m1.rolleHinzu(r[i]); System.out.println(m1); System.out.println(m1); try { em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.persist(m1); em.persist(m1); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); } catch (ConstraintViolationException (ConstraintViolationException e) { System.out.println( System.out.println(e.getMessage()); e.getMessage()); for( for(ConstraintViolation c:e.getConstraintViolations()) System.out.println( System.out.println(c.getMessage()); c.getMessage()); if (em.getTransaction(). em.getTransaction().isActive ().isActive()) isActive()) em.getTransaction(). em.getTransaction().rollback ().rollback(); rollback(); } } 0: XUrs Auftraege=[ ] Rollen=[ Java C ] Pruefe fuer XUrs Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 247 Beispiel: JPA mit Bean-Validation (5/5) public void beispieldaten(){ beispieldaten(){ Mitarbeiter m1 = new Mitarbeiter("X"); Rolle[] r= { new Rolle("C++", 60),new 60),new Rolle("C", 50) 50) ,new Rolle("Cobol", 70)}; for( for(int i=0;i<r.length;i i=0;i<r.length;i++) r.length;i++) m1.rolleHinzu(r[i]); System.out.println(m1); System.out.println(m1); try { em.getTransaction(). em.getTransaction().begin ().begin(); begin(); em.persist(m1); em.persist(m1); em.getTransaction(). em.getTransaction().commit ().commit(); commit(); } catch (ConstraintViolationException (ConstraintViolationException e) { for( for(ConstraintViolation c:e.getConstraintViolations()) System.out.println( System.out.println(c.getMessage()); c.getMessage()); if (em.getTransaction(). em.getTransaction().isActive ().isActive()) isActive()) em.getTransaction(). em.getTransaction().rollback ().rollback(); rollback(); 0: X Auftraege=[ ] Rollen=[ C++ C Cobol ] } Pruefe fuer X } echter Nachname TODO: Exception tritt erst bei DB-Nutzung aktuelle Sprachen auf, evtl. sinnvoll früher prüfen max 2 Rollen Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 248 Technischer Hinweis • Beispiele zeigen, dass die Version 4.2 und 4.3 der Bean Validation nicht mit EclipseLink 2.3.2 zusammenarbeiten • Lösung: Bean Validation 5.0.1 (wie vorher) oder 5.0.0 (Glassfish) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 249 Interessante weitere Features in JPA • in Anfrage-Sprache – Funktionen auf Datentypen z. B. SUBSTRING(String,Start,Ende) – UPDATE und DELETE in Querys • immer vor/nach Persistierung ausgeführte Methoden • Compound Primary Keys , zusammengesetzte Schlüssel über Hilfsklassen nutzbar • Verschiedene Sperrvarianten @Lob @Column @Column( Column(name="PIC") name="PIC") private byte[] byte[] picture; picture; @ManyToMany @JoinTable( JoinTable(name="PROJEKTROLLEN", name="PROJEKTROLLEN", joinColumns=@ joinColumns=@JoinColumn =@JoinColumn( JoinColumn(name=„ROLLEN_ID"), name=„ROLLEN_ID"), inverseJoinColumns=@ inverseJoinColumns=@JoinColumn =@JoinColumn( JoinColumn(name="PROJ_ID")) name="PROJ_ID")) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 250 Erinnerung: Eine Komponentenarchitektur-Variante • Datenbank als zentrale Kommunikationsplattform • Ansatz 1: alle Komponenten kennen Entitäten-Modell • Ansatz 2: Datenzugriffsschicht kapselt Datenbank Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 251 Ansatz 1: Alle kennen Entitäten-Modell (1/2) • Datenbank als zentrale Kommunikationsplattform • Entitäten sind bekannt, jede Komponente kann zugreifen • Benötigt Komponente weitere Eigenschaft einer Entität wird keine Spalte sondern Tabelle ergänzt (auch 1:1, 1:C, nutzt Primärschlüssel) • Sehr einfache Architektur • Sehr gut Komponenten an- und ausschaltbar • Keine Benachrichtigung bei Änderungen (sichtbar beim nächsten Lesen) • Stark eingeschränkte Wiederverwendbarkeit • Keine echte Informationskapselung • Wartbarkeit nimmt mit Systemgröße enorm ab Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 252 Ansatz 1: Alle kennen Entitäten-Modell (2/2) • Beispiel: Komponenten Produktverwaltung, Bestellsystem (sieht neue Produkte und Änderungen bei nächsten Aufruf) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 253 Ansatz 2: Kapselung mit Datenzugriffsschicht (1/2) • Kein direkter Zugriff auf Tabellen • Ermöglicht auch Benachrichtigungen (Observer) • Zerlegung in wiederverwendbare Komponenten • Unterschiedliche Varianten von Kommunikationen möglich – Nutzung einer zentralen Zugriffskomponente, die DB kapselt und Observer verwaltet – Nur Data Access Objects können miteinander reden – Komponentenschnittstellen unabhängig von der DB – Selbst gemeinsame Datenbank ist optional Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 254 Ansatz 2: Kapselung mit Datenzugriffsschicht (2/2) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 255 Überwachungsmethoden (1/3) @Entity public class Mitarbeiter { @Id @GeneratedValue( GeneratedValue(strategy= strategy=GenerationType.AUTO) GenerationType.AUTO) private int minr; minr; private String name; name; private void p(String s){System.out.println s){System.out.println(s);} System.out.println(s);} @PrePersist public void prePersit() prePersit() {p("prePersist {p("prePersist");} prePersist");} @PostPersist public void postPersist() postPersist() {p("postPersist {p("postPersist");} postPersist");} @PreRemove public void preRemove() preRemove() {p("preRemove {p("preRemove");} preRemove");} @PostRemove public void postRemove() postRemove() {p("postRemove {p("postRemove");} postRemove");} @PreUpdate public void preUpdate() preUpdate() {p("preUpdate {p("preUpdate");} preUpdate");} @PostUpdate public void postUpdate() postUpdate() {p("postUpdate {p("postUpdate");} postUpdate");} @PostLoad public void postLoad() postLoad() {p("postLoad {p("postLoad");} postLoad");} // Hinweis: Rollback bei einer Runtime Exception ... Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 256 Überwachungsmethoden (2/3) public static void main(String[] main(String[] args) args) { EntityManagerFactory emf = Persistence .createEntityManagerFactory(" createEntityManagerFactory("PrePostPU ("PrePostPU"); PrePostPU"); EntityManager em = emf.createEntityManager(); emf.createEntityManager(); Mitarbeiter m = new Mitarbeiter("Olga"); prePersist Mitarbeiter m2 = new Mitarbeiter("Otto"); prePersist EntityTransaction ta = em.getTransaction(); em.getTransaction(); prePersist ta.begin(); ta.begin(); postPersist em.persist(m em.persist(m); (m); postPersist em.persist(m2 em.persist(m2); (m2); postPersist em.persist( em.persist(new Mitarbeiter("Urs")); preUpdate ta.commit(); ta.commit(); postUpdate ta.begin(); ta.begin(); Mitarbeiter mm = em.find( em.find(Mitarbeiter.class, Mitarbeiter.class, m.getMinr()); m.getMinr()); mm.setName("Anna"); mm.setName("Anna"); em.persist(mm em.persist(mm); (mm); ta.commit(); ta.commit(); Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 257 Überwachungsmethoden (3/3) ta.begin(); ta.begin(); em.remove(m em.remove(m); (m); ta.commit(); ta.commit(); em.close(); em.close(); // notwendig für neuen Kontext em = emf.createEntityManager(); emf.createEntityManager(); for (Mitarbeiter m3 : em.createQuery( em.createQuery( "SELECT m FROM Mitarbeiter m" , Mitarbeiter.class) Mitarbeiter.class) .getResultList()) getResultList()) System.out.println(m3.getMinr System.out.println(m3.getMinr() (m3.getMinr() + ": " + m3.getName()); m3.getName()); em.close(); em.close(); emf.close(); emf.close(); preRemove postRemove postLoad postLoad 2: Otto 3: Urs } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 258