Object Relational Mapping

Werbung
ORM
Object Relational Mapping
oder
"The vietnam of computer science"
(http://blogs.tedneward.com/2006/06/26/The+Vietnam+Of+Computer+Science.aspx)
Das Problem
public class Maschine extends ModelElement
{
private int Zyklen;
private int CreateColor;
private ArrayList<Wuerfel> InputTemplate
= new ArrayList<Wuerfel>();
private ArrayList<Wuerfel> Input
= new ArrayList<Wuerfel>();
private boolean isWorking = false;
private int TimeToWork=0;
private Wuerfel Produkt = null;
private int WuerfelCount = 0;
Ein Tupel (4711,'SMITH',10) kann
als logische Aussage über die Welt
(siehe "Miniworld") interpretiert
werden, dass es einen Employee mit
Namen Smith und Nummer 4711
gibt, der in der Abteilung 10 arbeitet
public boolean working()
{
return isWorking;
}
OO-Programmierung:
- Objekte haben Identität, Zustand
und Verhalten
- Kapselung
- Vererbung, Polymorphie
Assoziationen
relationale Datenbanken
-Relationen enthalten Tupel (Aussagen)
-diese können durch Mengenoperationen transformiert und
kombiniert werden
object relational impedance mismatch
Lösungen
• Speicherung der Daten in einer
objektorientierten Datenbank (OODBMS)
(siehe VL Datenbanktechnik)
• Abbildung des Objektmodells auf
relationale Datenbank
• Abbildung des relationalen Datenmodells
auf Klassen
Objekte zu Relationen
viele Probleme ....
•
•
•
•
Klasse als Tabelle, Attribut als Spalte, AttributTypen als SQL-Typen
Vererbung, Polymorphie ?
Richtung von Assoziationen verschieden
Datenmodell doppelt
Daten doppelt (in DB und Objekten)
Konsistenz, etwa bei chaching
lazy evaluation nötig (Performance)
kann Performance verschlechtern
•
•
•
•
http://blogs.tedneward.com/2006/06/26/The+Vietnam+Of+Computer+Science.aspx
Relationen zu Klassen
OOAnwendung
"RelationsKlassen"
RDBMS
facade Pattern
Quelle: Wikipedia
CREATE TABLE EMP (
EMPNO INT PRIMARY KEY,
ENAME VARCHAR(50) NOT NULL,
DEPTNO INT REFERENCES DEPT(DEPTNO)
CREATE TABLE DEPT (
DEPTNO INT PRIMARY KEY,
DNAME VARCHAR(50) NOT NULL,
...
public class Emp {
private Long Empno;
private String Ename;
private Dept works_for;
wie kommen die Daten (der Zustand) in/aus der DB ?
public void create() {
....
smtm.execute("INSERT INTO EMP(EMPNO,ENAME,DEPTNO) VALUES .....
...
}
public void retrieve(Long Empno) ...
public void update() {
....
smtmt.execute("UPDATE EMP SET ... WHERE EMPNO = " +Empno
...
}
public void delete () ...
...
CRUD-Methoden
Techniken
•
•
•
manuell
•
•
•
mühsam, fehlerträchtig
bei (sehr) wenigen Klassen
Wartung, z.B. andere DB, Skalierung ...
problematisch
Generator (ggf. selbst entwickelt)
ORM-Tools
•
viele, z.B. Hibernate
Eigenentwicklung: Beispiel
Modelldefinition:
<TABLE name="Store">
<ATTRIBUTE name="ID" type="varchar" length="10" required="true"/>
<ATTRIBUTE name="Name" type="varchar" length="50" required="true"/>
<PRIMARYKEY name="pkStore" csep=";">
ID
</PRIMARYKEY>
</TABLE>
<TABLE name="Inventory">
<ATTRIBUTE name="ID" type="varchar" length="10" required="true"/>
<ATTRIBUTE name="ProductID" type="varchar" length="10" required="true"/>
<ATTRIBUTE name="StoreID" type="varchar" length="10" required="true"/>
<ATTRIBUTE name="Amount" type="integer" length="10" required="true"/>
<PRIMARYKEY name="pkAmount" csep=";">
ID
</PRIMARYKEY>
<FOREIGNKEY name="fkProduct" csep=";">
ProductID=Product.ID
</FOREIGNKEY>
<FOREIGNKEY name="fkStore" csep=";">
StoreID=Store.ID
</FOREIGNKEY>
</TABLE>
erzeugte Dateien
CREATE TABLE STORE (
ID varchar(10) NOT NULL,
NAME varchar(50) NOT NULL,
PRIMARY KEY (ID)
) TYPE=MyISAM;
CREATE TABLE INVENTORY (
ID varchar(10) NOT NULL,
PRODUCTID varchar(10) NOT NULL,
STOREID varchar(10) NOT NULL,
AMOUNT integer(10) NOT NULL,
PRIMARY KEY (ID)
) TYPE=MyISAM;
noname:~/Documents/BA/Vorlesungen/Middleware/Java/jdbl till$ ls erp
Inventory.java
POSLogs.java
Store.java
Inventorys.java
Product.java
Stores.java
POSLog.java
Products.java
nameConverter.java
Store.java
public class Store extends theObject implements Serializable
{
...
public void setID (String nInhalt)
throws unknownNameException, Exception
{
setAttrValue ("ID", nInhalt);
}
public String getID ()
throws unknownNameException, Exception
{
return (String) getAttrValue ("ID");
}
}
public void setName (String nInhalt)
throws unknownNameException, Exception
{
setAttrValue ("Name", nInhalt);
}
public String getName ()
throws unknownNameException, Exception
{
return (String) getAttrValue ("Name");
}
Verwendung
theFactory f = new theFactory (
new theMySQLDataStore ("localhost/erp", "till", "haenisch"),
new nameConverter ()
);
// Queries gehen immer ueber die Factory, die liefert einen Folder
// mit den Ergebnisobjekten zurueck
// Der erste Parameter ist der Name der Klasse, in der gesucht werden soll,
// der zweite kann eine SQL-where Bedingung enthalten
Stores ss = (Stores) f.doQuery ("Store", null);
// Durch Folder iterieren
while (ss.more ())
{
// aktuelles Objekt holen
Store sTemp = ss.next ();
System.out.print ("Store: " + sTemp.getID ()+" "+sTemp.getName ());
// Die untergeordneten (FOREIGN KEY) Objekte kriegt man mit getChildObjects:
Inventorys ivs = (Inventorys) sTemp.getChildObjects("Inventory");
int Bestand = 0;
}
// Und wieder durchlaufen, Bestaende addieren.
while (ivs.more())
{
Inventory iv = ivs.next();
Bestand += iv.getAmount().intValue();
}
System.out.println(" Bestand: " + Bestand);
Architektur
Applikation
Access-Layer
theFactory
Hält die Verbindung zur Datenbank, führt Queries durch...
theDataStore
Allgemeine Schnittstelle zum Datenzugriff
theOracleDataStore
theMySQLDataStore
ORACLE spezifisch
mySQL spezifisch
JDBC
Flexibilität
theFactory f = new theFactory (
new theOracleDataStore („susi:1521:SUSI", „user", „geheim"),
new lomi.nameConverter ()
);
Persons pes = (Persons) f.doQuery ("Person", null);
while (pes.more ())
{
Person pe = pes.next ();
System.out.println (pe.getVorname ()+" "+pe.getNachname ());
}
theFactory f = new theFactory (
new theMySQLDataStore "127.0.0.1/lomi", "root", null),
new lomi.nameConverter ()
);
theFactory f = new theFactory (
new theXMLDataStore "Daten.xml"),
new lomi.nameConverter ()
);
weiter
Applikation
Access Layer
(dlmeta)
RMIDataStore
TCP/IP
RMIDataStoreImpl
Irgendein
DataStore
Applikation
Access Layer
(dlmeta)
theMultiDataStore
Irgendein
DataStore
Irgendein
DataStore
Daten werden als
(dlmeta) XML übertragen
Irgendein
DataStore
Hibernate
Quelle: http://www.hibernate.org/hib_docs/reference/en/html/architecture.html
Genauer:
"Unfortunately, Hibernate is flexible and supports several approaches."
Quelle: http://www.hibernate.org/hib_docs/reference/en/html/architecture.html
Die Idee:
Quelle: http://www.hibernate.org/hib_docs/reference/en/html/architecture.html
Definition des Modells als Java-Klasse (POJO),
Mapping durch Annotations oder separate Datei
POJO = Plain Old Java
Objects
@Entity
public class Emp {
private Long Empno;
private String Ename;
private Long Deptno;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Long getEmpno() {
return Empno;
}
public void setEmpno(Long Empno) {
this.Empno = Empno;
}
oder
<hibernate-mapping>
<class name="Emp" table="EMP">
<id name="Empno" column="Empno" type="integer">
<generator class="native"/>
</id>
<property name="Ename" column="Ename" type="string" not-null="true"/>
</class>
</hibernate-mapping>
Verwendung
SessionFactory sessionFactory = new AnnotationConfiguration()
.addAnnotatedClass(Emp.class)
.buildSessionFactory();
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Emp e = new Emp();
e.setEmpno(e.getEmpno());
e.setEname("Haenisch");
e.setDeptno(20L);
session.save(e);
// und auslesen
List result = session.createQuery("from Emp").list();
for(int i=0;i<result.size();i++)
{
Emp p = (Emp)result.get(i);
System.out.println(" " + p.getEmpno() + " " + p.getEname() + " " + p.getDeptno());
}
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql:///ERP
hibernate.connection.username=till
hibernate.connection.password=haenisch
hibernate.current_session_context_class=thread
Problem (?)
export CLASSPATH=:/Hibernate/hibernate3.jar:/Hibernate/lib/jboss-system.jar:/Users/till/
Hibernate/hibernate-annotations-3.3.0.GA/hibernate-annotations.jar:/Users/till/Hibernate/
persistence-api-1.0.2.jar:/Hibernate/lib/dom4j-1.6.1.jar:/Users/till/Hibernate/hibernateannotations-3.3.0.GA/lib/hibernate-commons-annotations.jar:/Hibernate/lib/log4j-1.2.11.jar:/
Hibernate/lib/commons-logging-1.0.4.jar:/Users/till/Hibernate/mysql-connector-java-3.1.12bin.jar:/Hibernate/lib/commons-collections-2.1.1.jar:/Hibernate/lib/jta.jar:/Hibernate/lib/
cglib-2.1.3.jar:/Hibernate/lib/asm.jar:/Hibernate/lib/antlr-2.7.6.jar
komplexe Konfiguration, viele Abhängigkeiten
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file hibernate.log ###
#log4j.appender.file=org.apache.log4j.FileAppender
#log4j.appender.file.File=hibernate.log
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ##
log4j.rootLogger=warn, stdout
log4j.logger.net.sf.hibernate=info
### enable the following line if you want to track down connection ###
### leakages when using DriverManagerConnectionProvider ###
#log4j.logger.net.sf.hibernate.connection.DriverManagerConnectionProvider=trace
### log JDBC bind parameters ###
log4j.logger.net.sf.hibernate.type=info
### log prepared statement cache activity ###
log4j.logger.net.sf.hibernate.ps.PreparedStatementCache=info
ruby on rails: MVC
Quelle: http://java.sun.com/developer/technicalArticles/javase/mvc/
"Convention over Configuration"
"Don't repeat yourself"
"Principle of least surprise"
mysql> describe products;
+-------------+--------------+------+-----+---------+-------+
| Field
| Type
| Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| id
| varchar(10) | NO
| PRI |
|
|
| Name
| varchar(50) | YES |
| NULL
|
|
| Description | varchar(255) | YES |
| NULL
|
|
+-------------+--------------+------+-----+---------+-------+
3 rows in set (0.08 sec)
rails erp
script/generate controller Product
class ProductController < ApplicationController
def index
render_text "Produktverwaltung"
end
end
development:
adapter: mysql
database: erp
username: till
password: haenisch
socket: /tmp/mysql.sock
script/generate model Product
script/generate controller Product
class ProductController < ApplicationController
scaffold :Product
end
fertig ....
scaffolding:
(Übersetzung et wa
Gerüstbau)
rails erzeugt
Anwendungsgerüst,
Methode list usw.
eigenes Layout
list Methode überschreiben:
class ProductController < ApplicationController
scaffold :Product
end
def list
end
mit Editor
Template verwenden
Produkte suchen:
class ProductController < ApplicationController
scaffold :Product
def list
end
end
@products = Product.find_all
Im Template ausgeben:
<table>
<% @products.each do |product| %>
<tr>
<td><%=product.id%> </td>
<td><%=product.Name%></td>
<td><%=product.Description%></td></tr>
<% end %>
</table>
Ergebnis:
Herunterladen