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: