pdf

Werbung
Literatur
[12-1]
Müller, Bernd; Wehr, Harald: Java Persistence API 2. Hanser, 2012.
[12-2] Beeger, Robert et al.: Hibernate. Persistenz in Java-Systemen mit
Hibernate 3. dpunkt, 2006
[12-3]
http://www.tutorialspoint.com/jdbc
[12-4]
Ullenboom, Christian: Java 7 - Mehr als eine Insel. Galileo, 2012.
Kapitel 16
[12-5]
http://best-practice-softwareengineering.ifs.tuwien.ac.at/patterns/dao.html
[12-6] http://www.theserverside.de/singleton-pattern-in-java/
[12-7] http://mvnrepository.com/artifact/mysql/mysql-connector-java/5.1.34
… artifact/org.postgresql/postgresql/9.3-1102-jdbc41
[12-8] http://sqlzoo.net/wiki/Main_Page
Komponenten – WS 2014/15 – Teil 12/Persistenz
2
Übersicht
•
•
•
•
Das Object-Relationale Mapping (ORM)
JavaBeans
CRUD
Ein einfacher OR-Mapper
Komponenten – WS 2014/15 – Teil 12/Persistenz
3
Das Object-Relational-Mapping
• Object-Relational-Mapping = ORM = Verfahren, bei dem ObjektStrukturen in einer Objekt-orientierten Sprache auf die
Tabellenstruktur einer relationale Datenbank abgebildet und
damit speicher- und abrufbar werden
• Persistenz = Fähigkeit von etwas über die Laufzeit des
benutzenden Programms zu existieren
• Persistenz wird durch Speichern auf externen Medien, z.B.
Datenbanken oder Dateien realisiert.
• Persistente Objekte (in Java) = Erzeugte Objekte, die auf
externen Medien gespeichert sind und bei Bedarf in den
RAM/Heap geladen werden
• Siehe
– http://de.wikipedia.org/wiki/Objektrelationale_Abbildung
– http://de.wikipedia.org/wiki/Persistenz_(Informatik)
Komponenten – WS 2014/15 – Teil 12/Persistenz
4
Das CRUD-Prinzip
• CRUD = Abkürzung für
–
–
–
–
Create
Read bzw. Retrieve
Update
Delete
• Zu jedem persistenten Objekt gehört eine Implementierung, die
die vier CRUD-Operationen möglichst transparent für das Objekt
realisiert.
• Üblicherweise werden damit die Datenbank-Operationen zur
Behandlung von Records bezeichnet.
• Siehe
– http://de.wikipedia.org/wiki/CRUD
Komponenten – WS 2014/15 – Teil 12/Persistenz
5
JavaBeans
Eine einzelne Klasse ist dann eine JavaBean, wenn
•
•
•
•
nur private Attribute vorhanden sind,
alle Zugriffe auf Attribute über Getter/Setter laufen,
Vererbungen nicht durch Container-Bedingungen erzwungen sind
und wenn die Klasse serialisierbar ist, d.h. das Interface
java.io.Serializable realisiert.
Referenzierungen werden entsprechend dem Geflecht weiter verfolgt,
d.h. auch referenzierte Klassen müssen JavaBeans sein oder von der
Serialisierung ausgeschlossen werden.
Ein anderes Wort für JavaBean ist POJO (Plain Old Java Object).
Komponenten – WS 2014/15 – Teil 12/Persistenz
6
Wahl der Datenbank
Sie können benutzen:
• MySQL in der Version ab 5.4
• Postgresql in der Version 9.3.5
Hier wird MySQL benutzt, da diese aufgrund der Lehrveranstaltung
Webentwicklung bekannt ist.
7
Komponenten – WS 2014/15 – Teil 12/Persistenz
MySQL-Installationen (unter Windows) I
• MySQL selbst:
Am besten ist es, wenn XAMPP installiert wird:
http://sourceforge.net/projects/xampp/files/
• MySQL Workbench:
http://dev.mysql.com/downloads/workbench/
• MySQL Connector/J
http://dev.mysql.com/downloads/connector/j/
Zu allen drei Software-Paketen gibt es
auch Versionen für Linux und
für MacOS.
Komponenten – WS 2014/15 – Teil 12/Persistenz
8
MySQL-Installationen (unter Windows) III
Wer mit Maven arbeiten will (Empfehlung):
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
Dann wird der Konnektor in der angegebenen Version automatisch
herunter geladen und die manuelle Installation mit dem MySQL/JPaket entfällt.
Komponenten – WS 2014/15 – Teil 12/Persistenz
10
MySQL-Installationen (unter Windows) IV
Server lokal
starten
Dann über
phpMyAdmin die
den User root bzw.
einen anderen ohne
PW einrichten
Komponenten – WS 2014/15 – Teil 12/Persistenz
Das ist zwar unsicher,
aber lokal in Ordnung.11
Ein Beispiel für ein OR-Mapper
• Es wird ein einfacher OR-Mapper vorgestellt, der ca. 500 Zeilen
bei 23 Klassen groß ist.
• Daher können nicht alle Programmzeilen hier dargestellt werden.
Deshalb sollte die Implementierung von der Site des Dozenten
herunter geladen werden.
• Der Mapper erlaubt
–
–
–
–
alle Formen einer Klasse, also auch ohne Getter/Setter,
beliebige Geflechte, auch zirkular,
keine Vererbung,
ohne ID-Attribute auszukommen.
Komponenten – WS 2014/15 – Teil 12/Persistenz
12
Ein Beispiel – Event Manager I
Location
Appointment
Event
Description
Dies führt zu folgendem Geflecht von Objekten:
Location
Appointment
Event
Description
13
Komponenten – WS 2014/15 – Teil 12/Persistenz
Ein Beispiel – Event Manager II
public class Appointment implements java.io.Serializable {
@Attribute private Date date;
@Attribute private boolean existFlyer;
@Attribute private Timestamp created;
@Attribute transient private Location location;
@Attribute transient private Event event;
… Getter/Setter …
}
Kennzeichnung
der zu speichernden
Attribute
Ausschluss von
Serialisierung
Serialisierung
(Marker-Interface)
Damit alle (bis auf byte) primitiven Datentypen auftauchen, wurden einige
Erweiterungen durchgeführt, die vielleicht nicht immer plausibel sind; aber es
geht hier primär um die Demonstration, wie es geht.
Komponenten – WS 2014/15 – Teil 12/Persistenz
14
Ein Beispiel – Event Manager III
public class Event implements java.io.Serializable {
@Attribute private EventType mode;
@Attribute transient private Description description;
@Attribute private int maxPersons;
… Getter/Setter …
}
public class Description implements java.io.Serializable {
@Attribute private String text;
… Getter/Setter …
}
Hinweis:
Ohne transient wird beim Serialisieren das ganze Geflecht traversiert,
und nicht jeweils jede Klasse einzeln.
Deshalb muss bei jeder Referenz auf eine andere (Nicht-Standard-)Klasse
die Referenz mit transient modifiziert werden.
15
Komponenten – WS 2014/15 – Teil 12/Persistenz
Ein Beispiel – Event Manager IV
public class Location implements java.io.Serializable {
@Attribute private String street;
@Attribute private String houseNumber;
@Attribute("null") private char houseNumberExtension;
@Attribute private String city;
@Attribute private String postalCode;
@Attribute private float area;
… Getter/Setter …
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Attribute {
String value() default "notnull";
}
Komponenten – WS 2014/15 – Teil 12/Persistenz
Kennzeichnung,
ob Null-Werte
Zulässig sind
16
Implementierungsentscheidungen I
• Es werden JavaBeans mit/ohne Getter/Setter unterstützt.
• Die Klassen müssen serialisierbar sein.
• Alle zu "persistierenden" Attribute werden durch die Annotation
@Attribute gekennzeichnet, alle anderen Attribute werden
ignoriert.
• Es spielt keine Rolle, ob diese Attribute private, protected oder
public sind.
• Jede Klasse wird in einer eigenen Tabelle abgespeichert, die
genauso heißt wie die Klasse (Zeichensätze!).
• Es wird keine Vererbung berücksichtigt.
• Es werden keine inneren Klassen unterstützt.
• Die betroffenen Klassen brauchen keine die Persistenz
unterstützenden Attribute, wie z.B. id, enthalten.
• Die Struktur des Geflechts darf während der Residenz im RAM
nicht verändert werden. Spring erzeugt die Objekt-Geflechte.
17
Komponenten – WS 2014/15 – Teil 12/Persistenz
Implementierungsentscheidungen II
Bereich der
Objekte
Location
Appointment
Appointment
Location
Event
Description
Event
Description
Bereich der Tabellen
Komponenten – WS 2014/15 – Teil 12/Persistenz
18
Implementierungsentscheidungen III
Object-Referenzen werden durch Schlüssel ausgedrückt.
CREATE TABLE IF NOT EXISTS Appointment (
id INTEGER not NULL,
`date` DATETIME NOT NULL,
Referenzen sind
`existFlyer` BIT NOT NULL,
Integer-Fremdschlüssel
`created` TIMESTAMP NOT NULL,
`location` INT NOT NULL,
`event` INT NOT NULL,
PRIMARY KEY (id)
)
CREATE TABLE IF NOT EXISTS Location (
id INTEGER not NULL,
`street` VARCHAR(255) NOT NULL,
`houseNumber` VARCHAR(255) NOT NULL,
`houseNumberExtension` CHAR(1),
Schlüssel sind hier
`city` VARCHAR(255) NOT NULL,
immer Integer (32 bit)
`postalCode` VARCHAR(255) NOT NULL,
`area` REAL NOT NULL,
PRIMARY KEY (id)
)
Komponenten – WS 2014/15 – Teil 12/Persistenz
19
Implementierungsentscheidungen IV
Und sieht es in MySQL-Workbench aus:
Keine Beziehungen auf der Ebene der Datenbank, d.h. das Editieren kann
in beliebiger Reihenfolge ablaufen.
Komponenten – WS 2014/15 – Teil 12/Persistenz
20
Ein einfaches ORM I - Übersicht
BeanManager
Spring-DI
TableCreation
BeanWriter
BeanReader
HashCode
BeanRemover
TableInsert
TableUpdate
Session
BeanAdminList
JDBC
BeanAdminElem
ConfigDB
ConvertTypes
ClassManager
ClassDependency
21
Komponenten – WS 2014/15 – Teil 12/Persistenz
Klasse ConfigDB I – ein Singleton
Diese Klasse dient dazu, einmalig – global – die Datenbankparameter
bereit zu halten:
public class ConfigDB {
private static ConfigDB instance = null;
private
private
private
private
private
private
static
static
static
static
static
static
Parameter der
String user= "root";
String password= "";
Datenbank
String DBName= null;
final String driver= "com.mysql.jdbc.Driver";
final String protocol= "mysql";
final ConvertTypes convert= new ConvertTypesMySQL();
private ConfigDB() {}
public static ConfigDB getInstance() {
if(instance== null) {
instance= new ConfigDB();
}
return instance;
}
Komponenten – WS 2014/15 – Teil 12/Persistenz
SingletonMechanismus
Erzeugen der
Instanz
22
Klasse ConfigDB II – ein Singleton
Dies ist ein verkürzter Teil zum Setzen der Parameter:
public static void createInstance(String DBNamePar, String userPar,
String passwordPar) {
if(instance== null) {
if(DBNamePar!=null) {
DBName= DBNamePar;
}
Setzen der
if(userPar!=null) {
Parameter der
user= userPar;
}
Datenbank
if(passwordPar!=null) {
password= passwordPar;
}
}
}
… Getter/Setter …
23
Komponenten – WS 2014/15 – Teil 12/Persistenz
Klasse Session I
public class Session {
private Connection con= null;
private final ConfigDB DB;
private static boolean driverLoaded= false;
private boolean autoConnect= false;
Session() {
DB= ConfigDB.getInstance();
if(!driverLoaded) {
loadDriver(DB.getDriver());
}
}
private void loadDriver(String name) {
try {
Class.forName(name);
driverLoaded= true;
} catch (ClassNotFoundException ex) {
throw new IllegalArgumentException(...);
}
}
Komponenten – WS 2014/15 – Teil 12/Persistenz
Laden des
Treibers
24
Klasse Session II
private void connect2DB(boolean creating) {
String DSN= "jdbc:"+DB.getProtocol()+"://localhost";
if((DB.getDBName()!=null)&&!creating) {
DSN= "jdbc:"+DB.getProtocol()+"://localhost/"+DB.getDBName();
}
try {
con= DriverManager.getConnection(DSN,DB.getUser(),
DB.getPassword());
} catch (SQLException ex) {
throw new RuntimeException(...);
Verbindung zur
}
Datenbank
processWarnings();
herstellen
}
Der Parameter creating dient zum Anzeigen, dass eine Datenbank erzeugt
werden soll; dann darf der Datenbankname nicht in der DSN auftauchen,
Ansonsten muss er es.
Aus Gründen der Vereinfachung wird eine lokale Datenbank angenommen.
Ansonsten muss statt "localhost" die Adresse des Datenbankserver stehen.
25
Komponenten – WS 2014/15 – Teil 12/Persistenz
Klasse Session III - JDBC-Operationen
public boolean execute(String SQL) {
boolean problem= true;
try {
Statement stmt= con.createStatement();
problem= stmt.execute(SQL);
} catch (SQLException ex) {
throw new RuntimeException(....);
}
processWarnings();
return problem;
}
Nach diesem
Schema sind
alle JDBCAufrufe
strukturiert.
public int update(String SQL) {
int result= 0;
try {
Statement stmt= con.createStatement();
result= stmt.executeUpdate(SQL);
} catch (SQLException ex) {
… … …
}
Komponenten – WS 2014/15 – Teil 12/Persistenz
26
Klasse Session IV – JDBC-Query
public ResultSet query(String SQL) {
ResultSet result;
try {
Statement stmt= con.createStatement(
ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY);
result= stmt.executeQuery(SQL);
} catch (SQLException ex) {
… … …
}
Das ist die Query-Routine, bei der die Implementierungsentscheidung
getroffen wurde, dass das Result-Set nur von vorn nach hinten gelesen
werden darf (einfachste Version).
Komponenten – WS 2014/15 – Teil 12/Persistenz
27
Klasse Session V – Generieren des nächsten Schlüssels
public int nextIndex(String tblName) {
int maxValue= -1;
String SQL="SELECT MAX(id) FROM "+tblName+";";
checkForOpen(false);
ResultSet result= query(SQL);
Es wird ein der bisher höchste
try {
Index ausgelesen, dieser um 1
if(result.next()) {
maxValue= result.getInt(1); erhöht und dann zurück
geliefert.
}
result.close();
} catch (SQLException ex) {
throw new RuntimeException(...);
}
checkForClose();
return maxValue+1;
}
Das ist eine wichtige Methode, die tief in die Datenbank-Implementierung geht.
Es gibt auch andere Strategien zur Generierung des nächsten Indexes.
Komponenten – WS 2014/15 – Teil 12/Persistenz
28
Klasse Session VI
public void transaction() {
try {
con.setAutoCommit(false);
} catch (SQLException ex) {
throw new RuntimeException(...);
}
}
public void commit() {
try {
con.commit();
con.setAutoCommit(true);
} catch (SQLException ex) {
throw new RuntimeException(...);
}
}
public void rollback() {… analog …}
Ausschalten des
Autocommit
Einschalten des
Autocommit
AutoCommit bedeutet, dass direkt nach jedem SQL-Statement ein
Commit durchgeführt wird – ist eher MySQL-spezifisch.
29
Komponenten – WS 2014/15 – Teil 12/Persistenz
Zusammenfassung
getInstance
open()
ConfigDB
close()
CreateInstance(...)
execute(String)
update(String)
Session
query(String)
nextIndex(String)
transaction()
commit()
rollback()
Komponenten – WS 2014/15 – Teil 12/Persistenz
30
Ein einfaches ORM II - Übersicht
BeanManager
Spring-DI
TableCreation
BeanWriter
BeanReader
HashCode
BeanRemover
TableInsert
TableUpdate
Session
BeanAdminList
ConfigDB
JDBC
ConvertTypes
BeanAdminElem
ClassManager
ClassDependency
31
Komponenten – WS 2014/15 – Teil 12/Persistenz
BeanAdminList I
Location
Appointment
Event
Description
BeanAdmin
List
BeanAdmin
Elem
BeanAdmin
Elem
BeanAdmin
Elem
BeanAdmin
Elem
• In den Admin-Elementen werden ergänzende Informationen zu
den Knoten gehalten.
• Die Reihenfolge wird durch ein Pre-Order-Traversieren bestimmt.
Komponenten – WS 2014/15 – Teil 12/Persistenz
32
BeanAdminList II
Zeiger auf
das Objekt
public class BeanAdminElem {
public Object affectedObj;
public int identifier= -1;
public byte[] hash;
DatenbankIndex des Objekts
BeanAdminElem(Object affectedObj) {
this.affectedObj= affectedObj;
actualizeHash();
}
public boolean isModified() {
byte[] now= HashCode.get(affectedObj);
return !Arrays.equals(hash, now);
}
public final void actualizeHash() {
hash= HashCode.get(affectedObj);
}
Hash-Code des
Objektes
Überprüfen, ob der
Hash-Code sich
geändert hat
}
Das ist die Klasse der Elemente der Liste.
Es wird angenommen, dass gültige Tabellen-Indices positiv sind.
33
Komponenten – WS 2014/15 – Teil 12/Persistenz
BeanAdminList III
Object
• Der Hash-Code ist eine eindeutige
"Summe", die einen bestimmten
Inhalt charakterisiert.
• Anderer Inhalt führt zu einem anderen
Wert.
• Siehe:
– http://de.wikipedia.org/wiki/Hashfunkti
on
– http://de.wikipedia.org/wiki/MessageDigest_Algorithm_5
affectedObj
identifier
hash
BeanAdminElem
DB-Tabelle
Der Grund für den Hashwert liegt darin, dass überflüssige Schreiboperationen
auf der Datenbank vermieden werden sollen.
Komponenten – WS 2014/15 – Teil 12/Persistenz
34
BeanAdminList IV
public class BeanAdminList implements Iterator {
private final ArrayList<BeanAdminElem> beanList=
new ArrayList<>();
private boolean createMode= false;
private int listIndex= -1;
private int listSize= -1;
Diese Werte
werden für den
Iterator benötigt
public void create(Object obj, int identifier) {
BeanAdminElem elem= new BeanAdminElem(obj);
elem.identifier= identifier;
beanList.add(elem);
traverseBean(obj);
}
Erzeugen einer
neuen Liste
Komponenten – WS 2014/15 – Teil 12/Persistenz
35
BeanAdminList V
private void traverseBean(Object obj) {
for(Field attr : obj.getClass().getDeclaredFields()) { Prüft, ob der
Knoten schon
if(ClassManager.isComplexClass(attr.getType())) {
try {
einmal besucht
attr.setAccessible(true);
wurde
Object objNext= attr.get(obj);
if((objNext!=null)&&searchFor(objNext)==null) {
Reflexion
beanList.add(new BeanAdminElem(objNext));
Zugriff auf
traverseBean(objNext);
Variable
Fügt in
}
Liste ein
} catch (IllegalAccessException ex) {
… … …
}
Es werden die Deklarationen nach Verweisen auf Objekte rekursiv
traversiert, sofern der Knoten noch nicht besucht wurde.
Alle erzeugten Knoten werden in eine ArrayList eingefügt.
Das Pre-Order-Verfahren sollte hier deutlich sein.
Komponenten – WS 2014/15 – Teil 12/Persistenz
36
BeanAdminList VI – selbst geschriebener Iterator
@Override public boolean hasNext() {
Abfrage
return (listIndex<listSize);
}
@Override public BeanAdminElem next() {
if(listIndex>=listSize) {
Liefert das
throw new NoSuchElementException();
nächste Element
}
return beanList.get(listIndex++);
}
public void resetIterator() {
Lässt den
listIndex= 0;
Iterator neu
listSize= beanList.size();
beginnen
}
37
Komponenten – WS 2014/15 – Teil 12/Persistenz
Zusammenfassung
isModified()
release()
searchFor(Object)
Bean
Admin
List
actualizeHash()
Bean
Admin
Elem
create(Object,int)
hasNext()
next()
resetIterator()
getCreateMode()
setCreateMode(boolean)
Komponenten – WS 2014/15 – Teil 12/Persistenz
38
HashCode I
Serialisieren
public static byte[] get(Object obj) {
Hash-Funktion
byte[] digest;
erzeugen
try {
if(md5==null) {
md5= MessageDigest.getInstance("MD5");
}
ByteArrayOutputStream bout= new ByteArrayOutputStream();
ObjectOutputStream stream= new ObjectOutputStream(bout);
stream.writeObject(obj);
stream.flush();
digest = md5.digest(bout.toByteArray());
} catch (IOException | NoSuchAlgorithmException ex) {
throw new RuntimeException("Cant generate hashcode:"+ex);
Hash-Funktion
}
anwenden
return digest;
}
Es wird die kryptographische Hashfunktion MD5 benutzt.
Komponenten – WS 2014/15 – Teil 12/Persistenz
39
HashCode II
• Von der Benutzung von hashCode() wird abgeraten, da der
Algorithmus zu viele Kollisionen hat.
• Kollision = zwei oder mehrere unterschiedliche Originalwerte
ergeben denselben Hashwert
• MD5 erzeugt einen 128-bit-Hash.
• Für kryptographische Zwecke ist heute MD5 nicht zu empfehlen,
aber wir wollen hier lediglich die Gleichheit bzw. Modifikation
prüfen.
Komponenten – WS 2014/15 – Teil 12/Persistenz
40
Ein einfaches ORM III - Übersicht
BeanManager
Spring-DI
TableCreation
BeanWriter
BeanReader
HashCode
BeanRemover
TableInsert
TableUpdate
Session
BeanAdminList
ConfigDB
JDBC
ConvertTypes
BeanAdminElem
ClassManager
ClassDependency
Komponenten – WS 2014/15 – Teil 12/Persistenz
41
BeanWriter I
public class BeanWriter {
private final Session dbm= new Session();
public void write(BeanAdminList bal) {
dbm.open();
dbm.transaction();
if(bal.getCreateMode()) {
insert(bal);
bal.setCreateMode(false);
Transaktion
} else {
update(bal);
}
dbm.commit();
dbm.close();
}
In Abhängigkeit ob das Objekt erzeugt wird (CreateMode) oder nicht,
sind SQL Inserts oder Updates erforderlich.
Komponenten – WS 2014/15 – Teil 12/Persistenz
42
BeanWriter II - Insert()
private void insert(BeanAdminList bal) {
Erzeugen des
setIndeces(bal);
nächsten Index
bal.resetIterator();
while(bal.hasNext()) {
BeanAdminElem elem= bal.next();
TableInsert ti= new TableInsert(bal,elem.affectedObj,elem.identifier);
String SQL= ti.generateSQL();
Hilfsklasse zum
elem.actualizeHash();
Erzeugen des
dbm.update(SQL);
Setzen des
SQL-Statements
}
aktuellen
}
Hash-Wertes
Hier wird das Iterator-Interface von BeanAdminList benutzt.
Update sieht genauso aus, außer, dass eine andere Hilfsklasse
benutzt wird.
Komponenten – WS 2014/15 – Teil 12/Persistenz
43
BeanWriter III - TableInsert()
public class TableInsert extends TableCommon {
private final BeanAdminList bal;
TableInsert(BeanAdminList bal, Object obj, int identifier) {
… …
startPart= "INSERT INTO "+clazz.getSimpleName()+" (";
endPart= ") VALUES (";
firstTime= true;
}
public void add(String key, String value) {
if(firstTime) {
firstTime= false;
} else {
startPart+= ",";
endPart+= ",";
}
startPart+= ClassManager.encloseName(key,'`');
endPart+= value;
}
Komponenten – WS 2014/15 – Teil 12/Persistenz
44
BeanWriter IV - TableInsert()
public String generateSQL() {
ArrayList<Field> fields= getAnnotatedFields();
add(indexName,Integer.toString(identifier));
for(Field elem: fields) {
Attribute anno= elem.getAnnotation(Attribute.class);
Object objVal= getValue(elem);
AttributeType atype= ....determineAttributeType(elem.getType());
if(objVal==null) {
… …
add(elem.getName(),convert.attribute2SQL(atype, null));
} else {
if(atype==AttributeType.typClass){
BeanAdminElem ref= bal.searchFor(objVal);
… …
add(elem.getName(),convert.attribute2SQL(...));
} else {
add(elem.getName(),convert.attribute2SQL(atype, objVal));
}}}
return startPart+endPart+");";
}
Komponenten – WS 2014/15 – Teil 12/Persistenz
45
Bemerkungen
• Mit add() werden die einzelnen Elemente in den SQL-Ausdruck
gebracht.
• DetermineAttributeType() liefert in einem Aufzählungstyp den
Typ des betreffenden Elements.
public enum AttributeType {
typString, typFloat, typDouble, typInt, typBoolean, typLong, typChar,
typDate, typTimestamp, typEnum, typArray, typClass, typUnknown
}
• Mit attribute2SQL() wird ein Wert auf die Ebene von SQL
gebracht (Typen in SQL sind nicht dieselben wie in Java).
• Diese Konvertierung wird in einer Hilfsklasse versteckt.
Komponenten – WS 2014/15 – Teil 12/Persistenz
46
BeanWriter V - ConvertTypesMySQL()
class ConvertTypesMySQL implements ConvertTypes {
@Override public String attributeType2SQL(Class clazz) {
AttributeType atype;
switch(atype= ClassManager.determineAttributeType(clazz)) {
case typString:
return "VARCHAR(255)";
case typFloat:
return "REAL";
case typDouble:
return "FLOAT";
case typInt:
return "INT";
case typBoolean:
return "BIT";
case typLong:
return "BIG INT";
case typChar:
return "CHAR(1)";
case typDate:
return "DATETIME";
case typTimestamp: return "TIMESTAMP";
case typEnum:
return "VARCHAR(255)";
Dies ist die Abbildung
case typArray:
throw new ...
der Java-Datentypen
case typClass:
return "INT";
auf SQL-Datentypen
case typUnknown:
throw new ...
für die TabellenDefault:
throw new ...
generierung.
}
}
Komponenten – WS 2014/15 – Teil 12/Persistenz
47
BeanWriter VI - ConvertTypesMySQL()
String attribute2SQL(AttributeType atype, Object obj) {
...
switch(atype) {
case typString: return ClassManager.encloseName((String) obj,'\'');
case typFloat:
return Float.toString((float) obj);
case typDouble:
return Double.toString((double) obj);
case typInt:
return Integer.toString((int) obj);
case typBoolean:
return ((boolean) obj)? "1" : "0";
case typLong:
return Long.toString((long) obj);
case typChar:
...
Dies ist die Abbildung
der Java-Datentypen
}
auf SQL-Datentypen
}
für die Werte.
Komponenten – WS 2014/15 – Teil 12/Persistenz
48
Ein einfaches ORM IV - Übersicht
BeanManager
Spring-DI
TableCreation
BeanWriter
BeanReader
HashCode
BeanRemover
TableInsert
TableUpdate
Session
BeanAdminList
ConfigDB
JDBC
ConvertTypes
BeanAdminElem
ClassManager
ClassDependency
Komponenten – WS 2014/15 – Teil 12/Persistenz
49
BeanManager I
public class BeanManager {
private ApplicationContext appContext= null;
private final String SpringFile;
private final Class beanClass;
private boolean tablesExist= false;
private final Map<Object,BeanAdminList> myObjects= new HashMap<>();
public Object createBean(int identifier, String springName) {
initContext();
Spring
tablesExistence();
Object obj= appContext.getBean(springName);
CreateMode
BeanAdminList bal= new BeanAdminList(true);
bal.create(obj,identifier);
myObjects.put(obj, bal);
Ab in die HashMap
return beanClass.cast(obj);
}
Komponenten – WS 2014/15 – Teil 12/Persistenz
50
BeanManager II
public void writeBean(Object obj) {
tablesExistence();
BeanAdminList bal= myObjects.get(obj);
if(bal==null) {
throw new RuntimeException(...);
}
BeanWriter bw= new BeanWriter();
bw.write(bal);
}
Erzeuge Tabellen
falls erforderlich
BeanWriter
public void detachBean(Object obj) {
writeBean(obj);
BeanWriter
BeanAdminList bal= myObjects.get(obj);
myObjects.remove(obj);
bal.release();
Alles wegwerfen
}
Komponenten – WS 2014/15 – Teil 12/Persistenz
51
Bemerkungen
• Attached = Bean ist persistent und im Speicher
• Detached = Bean ist nur in der Datenbank
• Beans werden durch einen Integerwert, der gleichzeitig Schlüssel
in der ersten Tabelle ist referenziert.
Komponenten – WS 2014/15 – Teil 12/Persistenz
52
BeanManager III
private void tablesExistence() {
if(!tablesExist) {
Session dbm= new Session();
if(!dbm.existTable(beanClass.getSimpleName())) {
dbm.open();
preparePersistence(dbm);
Abfrage, ob schon
dbm.close();
Tabellen existieren
}
tablesExist= true;
}
}
Rekursives
Traversieren der
private void preparePersistence(Session dbm) {
Class-Definitionen
ClassDependency cd= new ClassDependency();
Class[] clist= cd.listConnectedClasses(this.beanClass);
for(Class elem: clist) {
TableCreation tbl= new TableCreation(elem);
dbm.update(tbl.generateSQL());
}
}
Komponenten – WS 2014/15 – Teil 12/Persistenz
53
Hilfsklasse: ClassDependency()
private ArrayList<Class> clazzList;
private void traverseClassDefinitions(Class clazz) {
clazzList.add(clazz);
for(Field attr : clazz.getDeclaredFields()) {
Class nowClazz= attr.getType();
if((attr.getAnnotation(Attribute.class)!=null)&&
ClassManager.isComplexClass(nowClazz)) {
if(!clazzList.contains(nowClazz)) {
traverseClassDefinitions(nowClazz);
}
}
Rekursives
} }
Traversieren der
Class-Definitionen
public Class[] listConnectedClasses(Class clazz) {
clazzList= new ArrayList<>();
traverseClassDefinitions(clazz);
return clazzList.toArray(new Class[clazzList.size()]);
}
ListConnectedClasses() wird von
preparePersistence() aufgerufen
Komponenten – WS 2014/15 – Teil 12/Persistenz
54
Das Hauptprogramm... I
App() {
System.out.println("Liste der Feten 2012");
ConfigDB.createInstance("EventDB", "root", "");
destroyDatabase();
createDatabase();
BeanManager ev= new BeanManager(Appointment.class, "Feten.xml");
Appointment ap1= (Appointment)ev.createBean(1, "IDApps");
setBeanValues(0, ap1);
printAppointments(new Appointment[] {ap1});
ev.detachBean(ap1);
ap1= (Appointment)ev.attachBean(1, "IDApps");
In setBeanValues werden die Werte der Objekte gesetzt.
Komponenten – WS 2014/15 – Teil 12/Persistenz
55
Das Hauptprogramm... II
ap1.setMaxPersons(100); //event
printAppointments(new Appointment[] {ap1});
ev.writeBean(ap1);
ap1.setExistFlyer(false);
ap1.setArea(0.2f);
ev.writeBean(ap1);
ap1.setHouseNumberExtension(' ');
ap1.setStreet("");
ap1.setDescription("Square Dance ab 7");
ev.writeBean(ap1);
ev.detachBean(ap1);
ap1= (Appointment)ev.attachBean(1, "IDApps");
printAppointments(new Appointment[] {ap1});
Komponenten – WS 2014/15 – Teil 12/Persistenz
56
Das Ganze noch einmal
BeanManager
Spring-DI
TableCreation
BeanWriter
BeanReader
HashCode
BeanRemover
TableInsert
TableUpdate
Session
BeanAdminList
ConfigDB
JDBC
ConvertTypes
BeanAdminElem
ClassManager
ClassDependency
Komponenten – WS 2014/15 – Teil 12/Persistenz
57
Nach dieser Anstrengung etwas Entspannung...
Komponenten – WS 2014/15 – Teil 12/Persistenz
58
Herunterladen