Softwaretechnik Anbindung an relationale Datenbanken Prof. Dr. Matthias Hölzl Joschka Rinke 13. Januar 2016 Anbindung an eine Datenbank Ziel Speicherung von persistenten Objekten, d.h. von Objekten des Anwendungskerns, die dauerhaft benötigt werden (z.B. Kunden, Konten, Flüge, Bücher, . . . ). Möglichkeiten I Relationale Datenbanken: Unterstützen Assoziationen und Vererbung nicht. Deshalb wird eine explizite Schnittstelle zwischen dem Anwendungskern und dem relationalen Datenbanksystem benötigt. I Objektrelationale Datenbanken: Erweitern relationale Datenbanken um objektorientierte Konzepte, meist Vererbung. I NoSQL-Datenbanken Sammelbegriff für Datenbanken, die nicht dem relationalen Modell folgen Varianten von NoSQL-Datenbanken I I I I I Objektorientierte Datenbanken: Unterstützen Assoziationen, Vererbung und Operationen. ODMG-Standard: ODL (object definition language) für Schemadeklarationen, OQL (object query language) und Sprachanbindungen Dokumentenorientierte Datenbanken: Enthalten nicht Tabellen mit vorgegebenem Schema sondern Dokumente, z.B. im XML oder JSON Format (CouchDB, MongoDB) Graphdatenbanken: Speichern Graphen, entweder als Knoten und Kanten, die beide durch Attribute annotiert werden können, oder als Tripel/Quadrupel (RDF, Abfragesprache SPARQL) Key-Value Datenbanken: Verwenden ein einfaches assoziatives Array als zugrundeliegendes Datenmodell (Berkeley DB, Apache Dynamo) Spaltenorientierte Datenbanken: Speichert Tabellen Spaltenweise, nicht zeilenweise (Cassandra, HBase) Anbindung an Relationale Datenbanken Abbildung eines Objektmodells auf Tabellen Voraussetzung Für jede Entity-Klasse A ist ein Primärschlüsselattribut aKey eingeführt. Abbildung von Klassen Tabelle A <<entity>> A aKey a1 ... an ... aKey a1 ... an Abbildung von Assoziationen Multiplizität * - * Verwendung einer eigenen Tabelle mit den Primärschlüsseln von A und B. A aKey ... * * B bKey ... aKey bKey Abbildung von Assoziationen Multiplizität * - 0..1 Primärschlüssel von B als Fremdschlüssel in die Tabelle von A aufnehmen. A aKey ... * 0..1 B bKey ... aKey a1 Abbildung von Assoziationen Multiplizität 0..1 - 0..1 Primärschlüssel von B in die Tabelle von A oder Primärschlüssel von A in die Tabelle von B aufnehmen. 0..1 A 0..1 aKey ... B aKey a1 ... an bKey bm aKey bKey ... oder bKey b1 ... Bemerkung A 0..1 1 B Bei ist der Primärschlüssel von B als Fremdschlüssel zur Tabelle von A hinzuzunehmen. Abbildung von Vererbung A key a B b C c Abbildung von Vererbung Variante I: Je eine Tabelle pro Klasse Tabelle A key a Tabelle B key b Tabelle C key c Nachteil: Bei Anfragen und Manipulationen, die Objekte einer Unterklasse betreffen, müssen ggf. Einträge in mehreren Tabellen berücksichtigt werden (z.B. bei Auswahl aller Attributwerte aller B-Objekte). Abbildung von Vererbung Variante II: Tabellen von Unterklassen enthalten geerbte Attribute Tabelle A key a Tabelle B key a Tabelle C b key a c Falls A abstrakt ist, genügt eine Tabelle pro Unterklasse (Tabelle A entfällt). Nachteil: Bei Änderungen an der Form der Oberklasse, müssen auch die Tabellen der Unterklassen verändert werden. Abbildung von Vererbung Variante III: Eine Tabelle für alle Ober- und Unterklassen Tabelle ABC key a b c Typ Nachteil: In Spalten, die für Objekte einer Unterklasse nicht relevant sind, müssen Nullwerte eingetragen werden. Datenbankanbindung mit JDBC I JDBC (Java Database Connectivity) bietet eine SQL-Schnittstelle für Java-Programme I JDBC ist unabhängig von einem konkreten (relationalen) Datenbanksystem. Zum Zugriff auf ein konkretes Datenbanksystem muss ein entsprechender Treiber geladen werden Schichten einer JDBC-Anwendung Java−Anwendungskern <<access>> JDBC−Datenbankschnittstelle java.sql::DriverManager JDBC−Treiber JDBC−Treiber DBMS DBMS java.sql JDBC-Kurzüberblick <<interface>> Connection DriverManager +getConnection(url: String): Connection +registerDriver(d: Driver) +getDriver(url: String): Driver <<interface>> Statement +createStatement(): Statement +executeQuery(sql: String): ResultSet +close() ... +executeUpdate(sql: String): Integer ... +getResultSet(): ResultSet ... * <<interface>> Driver +connect(url: String, info:Properties): Connection ... Liefert Daten der Spalte i der aktuellen Zeile als String−Objekt <<interface>> ResultSet +getString(i: Integer): String +getInt(i: Integer): Integer Geht zum ersten bzw. zum nächsten Datensatz ... +next(): Boolean JDBC-Kurzüberblick I Driver“, Connection“, Statement“ und ResultSet“ sind ” ” ” ” Schnittstellen, die von den Klassen eines geladenen Treiberpakets implementiert werden I Der DriverManager registriert Treiber für bestimmte DB-Systeme. Der Aufruf der statischen Operation getConnection“ stellt (mittels eines für die gegebene URL ” passenden Treibers) eine Verbindung zu einem DB-System her I Mittels eines Connection-Objekts kann ein Statement-Objekt erzeugt werden (Operation createStatement“) ” ←- JDBC-Kurzüberblick I I I I Mittels eines Statement-Objekts kann z.B. eine Anfrage an die DB gestellt werden (Operation executeQuery“) ” Die Ergebnistabelle der Anfrage wird in einem ResultSet-Objekt gespeichert Die Tabelle eines ResultSet-Objekts kann mit der Operation next“ zeilenweise durchlaufen werden ” Auf die Felder innerhalb einer Zeile kann mit einer (bzgl. des Spaltentyps) passenden get-Operation zugegriffen werden Beispiel: try { // Treiber auch von außen setzbar Class.forName("imaginary.sql.iMsqlDriver"); String url = "jdbc:msql://localhost/myDB"; Connection con = DriverManager.getConnection(url); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM test"); while (rs.next()) { // Zugriff auf die Spalte mit der Nummer y und dem Typ XXX // in der aktuellen Zeile XXX v = rs.getXXX(y); } con.close(); } catch (Exception e) { e.printStackTrace(); } Materialisierung von Objekten Gegeben Klassen im Anwendungskern: A B −aKey: String −bKey: String −a1: Typ1 ... −b1: Typ1 ... −an: Typn −bm: Typm +setAKey(x: String) +setBKey(x: String) +setA1(x: Typ1) ... +setB1(x: Typ1) ... ←- Materialisierung von Objekten Gegeben Tabellen in der relationalen Datenbank: Tabelle A aKey a1 ... Tabelle B an bKey b1 ... bm Materialisierung mit JDBC 1 RDBBroker {abstract } stmt = con.createStatement(); rs = stmt.executeQuery( "SELECT * FROM " + tabname() + " WHERE " + keyname() + " = " + "’" + key + "’"); if (rs.next()) return recordAsObject(); else return null; con = c; return "A"; #con + materialize(key: String): Object # recordAsObject(): Object {abstract} 0..1 −stmt # tabname(): String {abstract} 0..1 # keyname(): String {abstract} # rs ARDBBroker BRDBBroker + ARDBBroker(c: Connection) + BRDBBroker(c: Connection) # recordAsObject(): Object # recordAsObject(): Object # tabname(): String # tabname(): String # keyname(): String # keyname(): String return "aKey"; A a = new A(); a.setKey(rs.getString(0)); a.setA1(rs.getTyp1(1)); ... a.setAn(rs.getTypn(n)); return a; <<interface>> Connection <<interface>> Statement <<interface>> ResultSet Materialisierung mit JDBC try { String url = "jdbc:RDB-Typ//Rechner:Port/Datenbank"; Connection con = DriverManager.getConnection(url); ARDBBroker ardb = new ARDBBroker(con); BRDBBroker brdb = new BRDBBroker(con); A a = (A)ardb.materialize("xyz"); B b = (B)brdb.materialize("uvw"); } catch (Exception e) { e.printStackTrace(); } Materialisierung mit JDBC Bemerkungen I Zur Verbesserung der Effizienz werden häufig Zwischenspeicher (Cache) verwendet. I Bei der Materialisierung von Objektstrukturen werden häufig nur die gerade benötigten Objekte geladen (on-demand materialization). I Persistenz-Frameworks enthalten noch weitere Mechanismen z.B. zur Dematerialisierung und zur Transaktionskontrolle. Zusammenfassung I Zur Speicherung persistenter Objekte muss der Anwendungskern an eine Datenbank angeschlossen werden. I Dazu können relationale, objektrelationale oder NoSQL Datenbanken verwendet werden. I Bei der Verwendung einer relationalen Datenbank muss zunächst das Objektmodell auf Tabellen abgebildet werden. (Insbesondere müssen Vererbungshierarchien geeignet abgebildet werden!) I JDBC bietet eine plattformunabhängige Schnittstelle zur Anbindung von Java-Programmen an relationale Datenbanken. I Für das Object-Relational-Mapping“ (ORM) verwendet man ” meist Persistenz-Frameworks.