e-Commerce Datenbankanbindung mit JDBC Prof. Dr. Nikolaus Wulff Persistente Objekte • Objekte belegen einen bestimmten Speicherplatz und existieren eine gewisse Zeit lang während der Programmausführung. • Definition: – Persistenz speichert den Status und die Klasse eines Objektes durch Zeit und Raum. • Aufgabe eines Datenbanksystems ist es die Persistenz der Objekte zu gewährleisten, ohne das sich der Anwendungscode von dem transienter Objekte unterscheidet. Prof. Dr. Nikolaus Wulff e-Commerce 2 Objekte und Klassen • Objekte – haben eine eindeutige Identität (OID) – haben einen Status = Σ Attribute – haben ein Verhalten = Σ Methoden • Klassen – eine Klasse ist eine Menge von Objekten mit • einer gemeinsamen Struktur und • einem gemeinsamen Verhalten Prof. Dr. Nikolaus Wulff e-Commerce 3 Objekte und Klassen (I) P e rs o n Klasse: Objekt: n a m e : S trin g vo rn a m e : S trin g Statische Struktur w o h n s itz : Ad re s s e a lte r : in t Instanzen der Klasse niko:Person name Prof. Dr. Nikolaus Wulff = Wulff erwin:Person name = Müller vorname = Nikolaus vorname = Erwin wohnsitz= Lübeck wohnsitz= Hamburg alter alter = 39 e-Commerce = 45 4 Objekte und Klassen (II) Überführung des statischen OO-Modells in ein ER-Modell OO-Modell ER-Modell • • • • • • Klasse Instanz Attribut Prof. Dr. Nikolaus Wulff e-Commerce Tabelle Zeile Feld 5 Objekte und Klassen (III) • Verwaltung der ObjektID • Abbildung von Beziehungen – Abbildung von Vererbungsbeziehungen • Klassen erben die Attribute der Super-Klasse • Dynamisches Binden, Zugriff über die Super-Klasse – Assoziationen und Aggregation • Assoziation als Fremdschlüssel • Automatisches Nachladen von aggregierten Klassen • Objekt/Instanz - Abbildung – Abbildung der Attribute • Mapping => Zuordnen der Tabelle und Felder • Casting => Überführen auf Datentypen des RDBMS Prof. Dr. Nikolaus Wulff e-Commerce 6 Objekte und Klassen Objekt: niko:Person name = Wulff name = Müller vorname = Nikolaus vorname = Erwin wohnsitz= Lübeck wohnsitz= Hamburg alter alter Tabelle: OID name Xyz3450 Wulff uvq4711 Müller ... Prof. Dr. Nikolaus Wulff erwin:Person = 39 = 45 vorname wohnsitz alter Nikolaus Lübeck, ... 39 Erwin Hamburg 45 ... ... ... e-Commerce 7 ObjektID und Primärschlüssel Ist das Konzept des Primärschlüssels identisch zur ObjektID? ObjektID • ist eindeutig • unveränderlich • strukturlos Primärschlüssel • eindeutig pro Tabelle • kann sich ändern • je nach Tabelle anders • Der (fachliche) Primärschlüssel eignet sich nicht als OID. • Modellierung der OID als getrennte Klasse, mit • extra Spalte im ER-Modell (Forward-Engineering) • oder abgeleitet aus Tabelle und Primär-Schlüssel (Reverse-Engineering) Prof. Dr. Nikolaus Wulff e-Commerce 8 Klassendiagramm P e rso n F irm a nam e : S tring adres s e : A dres s e b e sch ä ftig t 1 1..* V e rtra g pos ition : S tring gehalt : float Prof. Dr. Nikolaus Wulff Prof. Dr. Nikolaus Wulff e-Commerce name : S tr ing vorn ame : S tr ing woh ns itz : A dres s e alter : int ER-Diagram Tabelle: Firma Vertrag 1:N Person 1:1 • Objekt Modell • ER- oder Tabellenmodell • SQL Code Zeitliche Entwicklung im Projekt CREATE TABLE Person person-id ID not null, vertrag-id ID not null, PRIMARY KEY (person-id), FOREIGN KEY (vertrag-id) REFRENCES Vertrag Prof. Dr. Nikolaus Wulff Prof. Dr. Nikolaus Wulff e-Commerce OO- und ER-Modell Koppelung • „SQL-Objekt“ – sehr enge Koppelung – Objekte beinhalten SQL-Code • Abstrakte DB-Superklasse – enge Koppelung – Jede Klasse kennt seine Tabelle – OO-Modell stark vom ER-Modell abhängig • Persistenzmodell – lose Koppelung => Schichtenarchitektur – BO-Klassen wissen nichts um ihre Persistenz Prof. Dr. Nikolaus Wulff e-Commerce 11 Schichtenarchitektur OO-Modell P e rso n ER-Modell L o a d e rP e rso n Person 1 ..* b e sch ä ftig t L o a d e rV e r tra g 1 F irm a Prof. Dr. Nikolaus Wulff L o a d e rF irm a e-Commerce Vertrag Firma 12 Persistenz - Framework DB - Klassen 1 < < d a ta b a se > > DBL o a d e r 0..* S m a rtPo i n te r O ID 1 1 O IDP e rso n O IDF irm a L o a d e rP e rso n L o a d e rF irm a Helper-Klassen < < p ro x y> > S P F irm a F irm a 1 0..1 < < p ro x y> > S P P e rso n 1..* P e rso n 1 Prof. Dr. Nikolaus Wulff 1 Business-Klassen 1 1..* b e sch ä ftig t 0..1 e-Commerce 13 JDBC Kernidee • Die Java DataBase Connectivity (JDBC) ist eine Abstraktionsschicht für relationale Datenbanken. • JDBC definiert einen Satz von Schnittstellen, die von entsprechenden Datenbankherstellern mit entsprechenden Treibern implementiert werden. • Dem Java Entwickler präsentiert sich immer die selbe SQL konforme Schnittstelle, unabhängig von der verwendeten Datenbank. • Somit lassen sich Anwendungen für verschiedenen Datenbanken wie DB2, Oracle, mySQL usw. vollkommen transparent programmieren. Prof. Dr. Nikolaus Wulff e-Commerce 14 JDBC Architektur Abstract Factory package java.sql DriverManager Factory SQLException SQLWarning MetaData 0..* <<Interface>> Driver DriverPropertyInfo 0..* <<create>> <<Interface>> Connection <<Inter face>> DatabaseMetaData <<derived>> <<create>> Command Iterator <<Interface>> Statement <<creat e>> <<Interface>> ResultSet <<derived>> <<Inter face>> ResultSetMetaData <<Interface>> PreparedStatement Prof. Dr. Nikolaus Wulff e-Commerce 15 Relational-Object-Mapping • JDBC bietet keine echte objektorientierte Abstraktion zur Persistenz von Business-Objekten. • Objekte haben per se keine 1:1 Abbildung auf relationale Datenbanken: – das Schreiben muss per Hand codiert werden. – der ResultSet enthält nur primitive Datentyen und die Objekte müssen per Hand zusammengesetzt werden • Diese Problem wird in der Literatur als Impedanzmissmatch bezeichnet. Prof. Dr. Nikolaus Wulff e-Commerce 16 Java SQL Typkonvertierung • = Konvertierung empfohlen x = Konvertierung möglich Identifizieren der Spalte über Namen oder Position. Große Daten- oder Textobjekte werden über Streams ausgelesen. Prof. Dr. Nikolaus Wulff e-Commerce 17 Einfaches Object Mapping • Im einfachsten Fall wird jede Klasse auf eine Tabelle abgebildet. • Jede Instanz/Objekt der Klasse bildet eine Zeile der Tabelle. • Jedes Attribut wird genau auf eine Spalte abgebildet. Dies setzt voraus, das es sich um einen primitiven SQL Datentyp handelt. Ansonsten muss ein Foreign Key verwaltet werden. • Meist wird für die Objektidentität ein künstlicher Primary Key vergeben. Prof. Dr. Nikolaus Wulff e-Commerce 18 Lesen einer Tabelle client driver : Driver Manager cont : Connection stm : Statement r s : ResultSet getConnection(String, String, String) createStatement( ) executeQuery(String) next( ) getObject(int) // read Table Resu ltSet rs = stm.executeQuery(where); wh ile(rs.next()) { for (int col=0; col<colums; col++) { Ob ject co lum = rs.getObject(i); ..... } // loop over columns } // loop over ro ws Prof. Dr. Nikolaus Wulff e-Commerce 19 BookStore Test TestCase (from j unit.framew ork) BookStoreTest tests -dri ver BookStore <<uses>> find/store <<Interface>> Driver (from j av a.sql) <<creates>> <<creates>> <<Interface>> Connection Book (from j av a.sql) Book attributs map to the ResultSet <<creates>> <<Interfac e>> ResultSet • Test mit JUnit. • Typische zu testende Operationen sind: (from j av a. sql) – – – – <<creates>> <<Interface>> Statement (from j av a.sql) findAll findByXXX store per update oder insert Löschen per delete Prof. Dr. Nikolaus Wulff e-Commerce 20 Testaufbau public class BookStoreTest extends TestCase { private private private private private final static String DRIVER = "org.gjt.mm.mysql.Driver"; final static String DBURL = "jdbc:mysql://localhost/bookstore" final static String USER = "bookstore"; final static String PWD = "bookstore"; BookStore bookstore; Verwendet wird der MySQL /** Treiber für die bookstore Datenbank. * @param arg0 URL, User und Passwort wie angegeben */ public BookStoreTest(String arg0) { super(arg0); } /** * Setup for the test. */ public void setUp() throws Exception { bookstore = new BookStore(DRIVER, DBURL, USER, PWD); } Prof. Dr. Nikolaus Wulff e-Commerce 21 Test von find und store /** * test the findAll method. * @throws Exception */ public void testFindAll() throws Exception { Collection result = bookstore.findAll(); assertTrue("result empty ", result.size() > 0); } /** * test the insert operation. * @throws Exception */ public void testInsert() throws Exception { String isbn = (new Date()).toString(); Book book = new Book(isbn, "junit", getName(), "JUnit Test"); bookstore.store(book); Collection result = bookstore.findByIsbn(isbn); assertTrue("insert not found ", result.size() == 1); } Prof. Dr. Nikolaus Wulff e-Commerce 22 BookStore Erzeugung public class BookStore { private final Driver driver; private final String url; private final String user; private final String password; /** * Constructor to initialize the BookStore database connection. * * @param driverName String classname of the JDBC driver * @param url String the url of the database * @param user String the user name of the database * @param password String the database password * @throws Exception any kind of exceptions to be thrown... */ public BookStore(String driverName, String url, String user, String password) throws Exception { Class clazz = Class.forName(driverName); this.driver = (Driver) clazz.newInstance(); this.url = url; this.user = user; this.password = password; } Prof. Dr. Nikolaus Wulff e-Commerce 23 Verbindung zur Datenbank /** * Returns a sql connection to the database. * * @return java.sql.Connection the connection to use * @throws java.sql.SQLException in case of an error */ private Connection getConnection() throws SQLException { Properties props = new Properties(); props.setProperty("user", user); props.setProperty("password", password); return driver.connect(url, props); } • Die Verbindung zur Datenbank wird für autorisierte User mit entsprechendem Paßwort erzeugt. • Im Fehlerfall wird eine SQLException geworfen. • Die Ressourcen müssen nach dem Gebrauch per close Befehl wieder freigegeben werden. Prof. Dr. Nikolaus Wulff e-Commerce 24 Resourcenfreigabe private void releaseResources(Connection con, Statement stm, ResultSet rs) { try { if (rs != null) rs.close(); } catch (Exception e) { handleError(e); } try { if (stm != null) stm.close(); } catch (Exception e) { handleError(e); } try { if (con != null) con.close(); } catch (Exception e) { handleError(e); } } Prof. Dr. Nikolaus Wulff e-Commerce 25 Generische Suche public Collection findByAuthor(String author) { String where = " where author='" + author + "'"; return find(where); } public Collection findByIsbn(String isbn) { String where = " where isbn='" + isbn + "'"; return find(where); } public Collection findByTitle(String title) { String where = " where title='" + title + "'"; return find(where); } Alle Suchoperationen verzweigen auf public Collection findAll() { die interne, generische find Operation. return find(""); } Zurückgegeben wird eine Collection gefüllt mit Büchern. Prof. Dr. Nikolaus Wulff e-Commerce 26 Die generische Suchroutine private Collection find(String where) { ResultSet set = null; Statement stm = null; Connection con = null; ArrayList bookList = new ArrayList(); String sql = "select * from books " + where; try { con = getConnection(); stm = con.createStatement(); set = stm.executeQuery(sql); while (set.next()) { long id = set.getLong("id"); String isbn = set.getString("isbn"); String author = set.getString("author"); String title = set.getString("title"); String description = set.getString("description"); bookList .add(new Book(id, isbn, author, title, description)); } } catch (SQLException e) { handleError(e); } finally { releaseResources(con, stm, set); } return bookList ; } Prof. Dr. Nikolaus Wulff e-Commerce 27 Generisches Speichern public void store(Book book) { if (findById(book.id.longValue()).size() == 0) { insert(book); } else { update(book); } } • Je nach dem ob das Buch schon in der Datenbank vorhanden ist oder nicht muss unterschiedlich vorgegangen werden: – Buch ist neu => Insert Statement – Buch ist alt => Update Statement Prof. Dr. Nikolaus Wulff e-Commerce 28 Speichern per Insert private void insert(Book book) { StringBuffer sql = new StringBuffer("insert into books "); sql.append("(isbn,author,title,description) "); sql.append(" values("); sql.append("'"); sql.append(book.isbn); sql.append("',"); sql.append("'"); sql.append(book.author); insert und update Methoden erzeugen sql.append("',"); ein Kommando, das an die executeSQL sql.append("'"); Methode übergeben & ausgeführt wird. sql.append(book.title); sql.append("',"); sql.append("'"); sql.append(book.description); sql.append("'"); sql.append(")"); // forward the sql string to the executeSQL method executeSQL(sql.toString()); Prof.} Dr. Nikolaus Wulff e-Commerce 29 Speichern per Update private void update(Book book) { StringBuffer sql = new StringBuffer("update books "); sql.append(" set isbn='"); sql.append(book.isbn); sql.append("', author='"); sql.append(book.author); sql.append("', title='"); sql.append(book.title); sql.append("', description='"); sql.append(book.description); sql.append("' where id="); sql.append(book.id); // forward the sql string to the executeSQL method executeSQL(sql.toString()); } insert und update Methoden erzeugen ein Kommando, das an die executeSQL Methode übergeben & ausgeführt wird. Prof. Dr. Nikolaus Wulff e-Commerce 30 Persistenz per Mapper • Der Bookstore dient als Fassade und beinhaltet die Logik zum Speichern/Laden der Business-Objekte. • Für einfache Anwendungen ist dies ausreichend, belastet jedoch bei vielen verschiedenen Objekten den Bookstore mit zu viel JDBC Code und Detailwissen über die innere Struktur der Klassen. • Abhilfe: Einführen einer Persistenzabstraktion. • Einfachste Version: Strategie-Muster Der Bookstore verwaltet eine Map mit zu den Business-Objekten passenden Persistenz-Mappern, an die entsprechende DB Befehle delegiert werden. Prof. Dr. Nikolaus Wulff e-Commerce Persistenz Framework • Datenbank Funktionalität in spezielle Mapper Klassen auslagern. • Einfachste Version: – Alle BusinessObjekte erben von einer PersistenzKlasse. – Alle Mapper erben von einem PersistenzMapper. – Lediglich einige spezielle Methoden müssen überladen werden. • Vorteil: – Modulare Mapper, einfache Implementierung • Nachteil: – Kopplung der BO-Klassen an PersistenzKlasse Prof. Dr. Nikolaus Wulff e-Commerce Java Templates • Der BookStore verwaltet eine Map mit zu den Business Objekten passenden Mappern. • Mit Hilfe von Java Generics läßt sich ein solcher Persistenz Mechanismus als Template typsicher implementieren. • Ein PersistenzMapper wird als abstraktes Generic definiert, dem nur noch einige spezialisierte Mapping Funktionen fehlen, die in abgeleiteten Klassen implementiert werden. Prof. Dr. Nikolaus Wulff e-Commerce BookStore Mapper Verwaltung • Der BookStore delegiert alle DB Aktionen an generische Mapper Instanzen vom Typ <T>. • Als Schlüssel dient die Class<T> Instanz des Objekts T. BookStore store(obj : PersistentObject) : void delete(obj : PersistentObject) : void type : Class<T> Map 1 <T extends PersistentObject> PersistentMapper store(obj : T) : void delete(obj : T) : void Prof. Dr. Nikolaus Wulff mapps e-Commerce PersistentObject PersistentMapper Template <T extends PersistentObject> <<abstract>> PersistentMapper table : St ring scheme : S tring type : Clas s<T> connect ion : java. sql.Connect ion Persis tent Object id : long = -1 PersistentMapper(type : Class<T>, t able : S tring, schem e : St ring) <<abstract>> insert (obj : T) : void <<abstract>> updat e(obj : T) : void <<abstract>> mappToInstance(set : Result Set) : T isNew(obj : T) : boolean setId(obj : T) : void store(obj : T) : void delete(obj : T) : void execut eQuery(where : String) : Collection<T> execut eSQL(sql : S tring) : void PersistentMapper<Book> Persistent Mapper<User> getId() setId() PersistentMapper<Order> BookMapper insert(obj : Book) : void update(obj : Book ) : void mappToIns tance() : Book Book UserMapper User insert(obj : User) : void update(obj : User) : void mappToInst ance() : User OrderMapper insert(obj : Order) : void update(obj : Order) : void mappToInst ance() : Order Prof. Dr. Nikolaus Wulff e-Commerce Order Adapter als Dekorierer • Trennung von fachlicher und technischer Schnittstelle. • Anstatt die Business-Objekte von einer PersistentObject Superklasse abzuleiten kann das Adapter Muster zur Entkoppelung verwendet werden. • Ein Adapter dekoriert die fachlichen BO-Instanzen mit der technischen Funktionalität des Persistenzframeworks. • BO-Klassen enthalten selber keinen Hinweis auf eventuelle Datenbank Aktionen. • Java EJBs und OR Mapper folgen diesem Ansatz in unterschiedlicher Ausprägung. • Die wesentlichen Ideen lassen sich einfach codieren oder noch besser per Annotations oder XDoclet generieren. Prof. Dr. Nikolaus Wulff e-Commerce Klassen-Adapter als Dekorierer Book Fachliche Schnittstelle Technische Schnittstelle <<Interface>> PersistentObject <T> PersistentMapper markDirty() setId() getId() PersistentMapper<Book> BookMapper Prof. Dr. Nikolaus Wulff Persistent Book e-Commerce Objekt-Adapter als Dekorierer <<Interface>> Book 1 BookImpl Fachliche Schnittstelle Technische Schnittstelle <T> PersistentMapper <<Int erface>> PersistentObject PersistentMapper<Book> BookMapper Prof. Dr. Nikolaus Wulff PersistentBook e-Commerce Anwendung eines Mappers • Unabhängig von der speziellen Realisierung der Mapper gestaltet sich eine Anfrage recht einfach: public Collection<Book> findBooksByTitle( String title) { PersistentMapper<Book> mapper; String where = " WHERE title='" + title + "'"; try { mapper = getMapper(Book.class); return mapper.executeQuery(where); } catch(SQLException e) { handleError(e); return null; } } Prof. Dr. Nikolaus Wulff e-Commerce