vierseitig

Werbung
3. Java Persistence API ( JPA )
•
•
•
•
•
•
•
•
Literatur
Problem: OO in relationale DB
Idee des Persistence Mapper
Einfache Entity-Klasse
Lebenslauf eines Entity-Objekts
Umsetzung von 1:N- und M:N-Relationen
Konsistenzüberwachung
Vererbung
Anfragen
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
• Sun Microsystems, JSR 220: Enterprise JavaBeans, Version
3.0, Java Persistence API (ejb-3_0-fr-spec-persistence.pdf,
ist der Standard; trotzdem sehr gut lesbar!)
http://jcp.org/aboutJava/communityprocess/final/jsr220/index.html
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
74
Komponentenbasierte
Software- Entwicklung
75
Prof. Dr.
Stephan Kleuker
Nutzung relationaler DB in OO -Programmen
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)
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
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
76
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
77
Aufgabe und Ansatz
Lagerverwaltung ( ein kleines OO- Modell )
Lagerraum
-nummer : String
beinhaltet
-artikel
1
*
+verkaufswertBerechnen() : float
+Produkt() : Produkt
+verfuegbareAnzahl() : int
+verkauft(anzahl : int) : void
+einlagern(anzahl : int) : void
+verkaufspreis() : 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
Software- Entwicklung
Produkt
-name : String
-lagermenge : int
-preis : float
Buch
+verkaufspreis() : float
78
Prof. Dr.
Stephan Kleuker
Umgang mit Kapselung
• Objekte der Klasse Produkt können leicht
in folgende Tabelle übersetzt werden.
Produkt
OID
name
lagermenge
preis
• 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
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
79
Anmerkungen
Produkt
-name : String
-lagermenge : int
-preis : float
+Produkt() : Produkt
+verfuegbareAnzahl() : int
+verkauft(anzahl : int) : void
+einlagern(anzahl : int) : void
+verkaufspreis() : float
• 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
• 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
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
80
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
81
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
Umgang mit Polymorphismus
• Wie funktioniert Polymorphismus?
Produkt
Lebensmittel
-verfallsdatum : Date
+verlustAm(stichtag : Date) : float
• 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
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
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
public float verkaufswertBerechnen(){
float ergebnis=0.0f;
for(Produkt tmp:artikel)
ergebnis= ergebnis+
tmp.verfuegbareAnzahl()*tmp.verkaufspreis();
return ergebnis;
}
82
Umgang mit Objektidentität
Komponentenbasierte
Software- Entwicklung
83
Prof. Dr.
Stephan Kleuker
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)
• 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)
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
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
84
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
85
Bearbeiten von Objekten
Zwischenfazit
• 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
• 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, TopLink) , JPA spezifiziert
gemeinsames Interface
SELECT name, lagermenge, preis, verfallsdatum
FROM Identifikation, Lebensmittel, Produkt
WHERE Identifikation.Name = 'banane'
AND Identifikation.OID = Produkt.OID
AND Produkt.OID = Lebensmittel.OID
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
• Verwandte Ansätze: direkte Nutzung einer OO-Datenbank,
Programmierung mit JDO
86
Konzept von Persistence - Mappern
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
87
Beispiel ( 1 / 6 )
package jpabeispiel1;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Mitarbeiter {
@Id private int minr;
private String name;
• 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)
public Mitarbeiter(){} //leerer 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
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
88
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
89
Beispiel ( 2 / 6 )
Beispiel ( 3 / 6 )
• Persistence.xml liegt im Ordner META-INF (projektartabhängig)
• Detaileinstellungen von JPA-Realisierung abhängig (z. B.
Hibernate, TopLink)
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="JPABeispiel1PU"
transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>jpabeispiel1.Mitarbeiter</class>
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
90
Beispiel ( 4 / 6 )
<properties>
<property name="hibernate.connection.username"
value="kleuker"/>
<property name="hibernate.connection.driver_class"
value="org.apache.derby.jdbc.ClientDriver"/>
<property name="hibernate.connection.password"
value="kleuker"/>
<property name="hibernate.connection.url"
value="jdbc:derby://localhost:1527/ProjVer"/>
<property name="hibernate.cache.provider_class"
value="org.hibernate.cache.NoCacheProvider"/>
<property name="hibernate.hbm2ddl.auto"
value="update"/>
</properties>
</persistence-unit>
</persistence>
Komponentenbasierte
Software- Entwicklung
91
Prof. Dr.
Stephan Kleuker
Beispiel ( 5 / 6 )
package jpabeispiel1;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public void datenZeigen() {
for (Mitarbeiter m : (List<Mitarbeiter>)
em.createQuery("SELECT m FROM Mitarbeiter m").
getResultList()) {
System.out.println(m.getMinr() + ": " + m.getName());
}
}
public class Main {
private EntityManagerFactory emf = Persistence.
createEntityManagerFactory("JPABeispiel1PU");
private EntityManager em = emf.createEntityManager();
public static void main(String[] args) {
Main m = new Main();
m.beispieldaten();
m.datenZeigen();
m.beenden(); // mit em.close()
}
public void beispieldaten() {
String namen[] = {"Egon", "Erwin", "Ute", "Aische"};
em.getTransaction().begin();
for (int i=0; i<namen.length; i++)
em.persist(new Mitarbeiter(i,namen[i]));
em.getTransaction().commit();
}
0:
1:
2:
3:
Egon
Erwin
Ute
Aische
}
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
92
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
93
Beispiel ( 6 / 6 )
Suchen und Bearbeiten von einzelnen Objekten
public void namenAendern(){
int eingabe=-1;
while(eingabe!=0){
System.out.print("Welche Nummer (Ende mit 0): ");
eingabe=new Scanner(System.in).nextInt();
Mitarbeiter m = em.find(Mitarbeiter.class, eingabe);
if(m == null)
System.out.println("Witzbold");
else{
System.out.print("Neuer Name (alt:"+m.getName()+"): ");
String name=new Scanner(System.in).next();
EntityTransaction tr = em.getTransaction();
tr.begin();
m.setName(name);
tr.commit();
}
}
• Falls noch keine Tabelle Mitarbeiter existiert, wird diese
angelegt
}
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
94
Entity ist POJO
Prof. Dr.
Stephan Kleuker
95
Primary Key
Entity-Klassen-Objekte sind klassische Plain Old Java Objects
• Verpflichtung:
– public (oder protected) Default-Konstruktor
– 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
Software- Entwicklung
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
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
96
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
97
Persistierbare Typen / Klassen
Persistent Fields / Properties
• 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
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
98
Annotationen zur Flexibilisierung
Prof. Dr.
Stephan Kleuker
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
99
PersistenceContext
@Entity
@Table(name="Chef")
public class Mitarbeiter implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int minr;
@Column(name="Leiter", nullable=false,
updatable=true, unique=true)
private String name;
...
Komponentenbasierte
Software- Entwicklung
• 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
•
•
•
•
•
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.flush(); // in Transaktion
em.getTransaction().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)
100
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
101
Beispiel fü r Cache ( 1 / 2 )
Beispiel fü r Cache ( 2 / 2 )
@Entity
public class Mitarbeiter implements Serializable {
public static void main(String[] args) {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("JPACachePU");
EntityManager em=emf.createEntityManager();
em.getTransaction().begin();
em.persist(new Mitarbeiter("ET"));
em.persist(new Mitarbeiter("JFK"));
for(int i=1;i<3;i++)
System.out.println(em.find(Mitarbeiter.class,i));
em.getTransaction().commit();
em.close();
}
ET(1)
}
JFK(2)
org.hibernate.util.JDBCExceptionReporter logExceptions
WARNUNG: SQL Error: -1, SQLState: 22001
SCHWERWIEGEND: Bei dem Versuch, VARCHAR 'JFK' auf die
Länge 2 zu kürzen, ist ein Abschneidefehler aufgetreten.
@Id @GeneratedValue
private int minr;
@Column(length=2) //maximal zwei Zeichen
private String name;
public Mitarbeiter() {
} //leerer Konstruktor benötigt
public Mitarbeiter(String name) {
this.name = name;
}
// get- und set-Methoden weggelassen
public String toString(){
return name+"("+minr+")";
}
}Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
102
Lebenslauf eines Entity - Objekts
Prof. Dr.
Stephan Kleuker
Prof. Dr.
Stephan Kleuker
103
Seltsames merge
NEW -> merge() führt evtl. zur Mehrfachobjekterzeugung
refresh() nur, wenn vorher persistiert
Komponentenbasierte
Software- Entwicklung
Komponentenbasierte
Software- Entwicklung
104
public static void main(String[] args) {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("JPAMergePU");
EntityManager em = emf.createEntityManager();
Mitarbeiter m1 = new Mitarbeiter("Ford");
Mitarbeiter m2 = new Mitarbeiter("Arthur");
em.getTransaction().begin();
em.merge(m1);
em.merge(m1);
1: Ford
em.merge(m1);
2: Ford
em.persist(m1);
3: Ford
em.merge(m1);
em.persist(m2);
4: Ford
em.merge(m2);
5: Arthur
em.getTransaction().commit();
for (Mitarbeiter m : (List<Mitarbeiter>) em.
createQuery("SELECT m FROM Mitarbeiter m").
getResultList())
System.out.println(m.getMinr() + ": " + m.getName());
}
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
105
Wichtiges / korrektes merge
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
public static void main(String[] args) {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("JPAMergePU");
EntityManager em = emf.createEntityManager();
Mitarbeiter m1 = new Mitarbeiter("Ford");
em.getTransaction().begin();
em.persist(m1);
em.getTransaction().commit();
Mitarbeiter m3= em.find(Mitarbeiter.class,1);
m1.setName("Trilian");
em.merge(m1);
System.out.println(m3.getMinr() + ": " + m3.getName());
// trotzdem besser: Änderungen in Transaktionen
em.close();
1: Trilian
}
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
106
Generelle JEE - Regel
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
107
Einschub : XML- Konfiguration
Convention over Configuration
• bedeutet: wenn nichts angegeben wird, wird ein DefaultWert genutzt
• Default-Werte sind zwar sinnvoll, sollte man aber kennen
• Statt Annotationen zu nutzen, können diese Informationen
auch in XML beschrieben werden
• Typisch: einen XML-Datei pro Klasse + zusammenführende
XML-Datei
• Vorteil: Verhaltensänderungen ohne Codeänderung
• Nachteil: viele kleine penibel zu pflegende Dateien
• Erinnerung: Java-Inkonsistenz
• Auch möglich: XML und Annotationen; dabei „schlägt“ XML
die Annotationen
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
108
Komponentenbasierte
Software- Entwicklung
Prof. Dr.
Stephan Kleuker
109
Herunterladen