Algorithmen und Datenstrukturen II Alexander Goesmann Bioinformatics Resource Facility Center for Biotechnology Universität Bielefeld Vorlesung Sommer 2010
Überblick Datenspeicherung in relationalen Datenbanken
Persistente Datenspeicherung in Java mit JDBC
ORM – Objekt-relationales Mapping
Datenspeicherung mit Hibernate
Datenspeicherung in relationalen Datenbanken Teil I
Relationale Datenbanken Relationale Datenbank dient zur elektronischen Datenverwaltung in Computersystemen Beruht auf relationalem Datenbankmodell von Edgar F. Codd aus dem Jahr 1970 Basiert auf Relationen und Operationen darauf (Vgl. relationale Algebra) Verwaltung der gespeicherten Daten mit relationalem Datenbankmanagementsystem (RDBMS) Abfrage und Manipulation der Daten mit Datenbanksprache SQL (Structured Query Language)
Grundlegende Konzepte Relationale Datenbank ist Sammlung von Tabellen (Relationen) mit gespeicherten Daten Jede Zeile (Tupel) in einer Tabelle ist ein Datensatz (record) Tupel besteht aus Reihe von Attributwerten (Attribute = Eigenschaften), den Spalten der Tabelle Relationenschema legt Anzahl und Typ der Attribute für eine Relation fest Eindeutige Identifizierung von Datensätzen über Schlüssel (key)
Beispiel
Quelle: Wikipedia
Beziehungen zwischen Tabellen Redundante Speicherung von Daten vermeiden Verwendung eines eindeutigen Schlüssels pro Tabelle zur Identifizierung des Datensatzes (primary key) Verweis aus anderen Tabellen auf Daten mit Hilfe dieses Schlüssels Derartige Attribute werden als Fremdschlüssel (foreign key) bezeichnet Tabellen ohne Fremdschlüssel heißen flache Tabellen
Tabelle anlegen Syntax: CREATE TABLE " " "
"Tabellen_Name"
"("Spalte 1" "Datentyp_für_Spalte_1",
" "Spalte 2" "Datentyp_für_Spalte_2",
" ... )"
Beispiel: CREATE TABLE Nutzer (Nutzer-ID int,
" " "Vorname varchar(50), "
"
"
"Nachname varchar(50))
"
Daten einfügen Syntax: INSERT INTO
" ( [, weitere Spaltennamen])
VALUES ( [, weitere Werte])"
Beispiel: INSERT INTO Nutzer (Nutzer-ID, Vorname, Nachname)
" VALUES (13, 'Hans', 'Müller')"
Daten abfragen Syntax: SELECT ... FROM ... WHERE
" [ ]"
Beispiele: Select * FROM Nutzer" Select * FROM Nutzer WHERE Vorname = „Hans“" Select Autor, Titel FROM Bücher WHERE Verlagsjahr > 2000
"AND Verlag != „KVG“"
Daten aktualisieren Syntax: UPDATE SET =
[, weitere Spaltennamen = Ausdruck] WHERE "
Beispiele: UPDATE Bücher SET Datum = „20.07.2010“" UPDATE Bücher SET Datum = Datum + 2000 WHERE Datum < 20"
Daten löschen Syntax: DELETE FROM WHERE "
Beispiele: DELETE FROM Nutzer" DELETE FROM Entliehen WHERE Nutzer-ID = 12"
Weiterführende Themen Zusammengesetzte Schlüssel Indexierung JOINs Komplexe Abfragen und Unterabfragen Normalisierung & Normalformen Optimierung Gespeicherte Funktionen und Prozeduren
Persistente Datenspeicherung in Java mit JDBC Teil II
JDBC Java Database Connectivity Einheitliche Datenbankschnittstelle zu DBMS verschiedener Hersteller Java Application JDBC
Client Machine
RDBMS proprietary protocol RDBMS
Database Server
JDBC Treiber Für jedes DBMS ist ein JDBC-Treiber nötig Typ 1 – bildet die JDBC API auf andere native API ab Typ 2 – teilweise in Java, teilweise in nativem Code geschrieben Typ 3 – vollständig in Java geschrieben, kommuniziert über einen middleware-server über ein datenbankunabhängiges Protokoll Typ 4 – vollständig in Java geschrieben, implementiert das jeweilige Datenbank-Protokoll
Verbindung herstellen Treiber laden Class.forName(„com.mysql.jdbc.Driver “) Treiber registriert sich beim Laden selbstständig beim DriverManager
Verbindungs-URL jdbc::[propertylist] z.B. jdbc:mysql:testdb
DriverManager vs. DataSource
DriverManager Treiber registrieren sich beim DriverManager Erhält Verbindungs-URL Optional Benutzername, Passwort Löst den Treiber über Namen in URL auf Beispiele Connection con = DriverManager.getConnection („jdbc:derby:COFFEES“) DriverManager.getConnection („jdbc:derby:COFFEES“, „user“, „password“)
DataSource Interface Beschreibt eine Verbindung zur Datenbank Wird häufig außerhalb der Anwendung definiert und z.B. über Java Naming and Directory Interface (JNDI) abgefragt (hängt vom verwendeten Framework ab) Beispiel
InitialContext ic = new InitialContext(); DataSource ds = ic.lookup("java:comp/env/ jdbc/myDB"); Connection con = ds.getConnection();
Programmatische DataSource Beispiel: ClientDataSource ds = new org.apache.derby.jdbc.ClientDataSource(); ds.setPort(1527); ds.setHost("localhost"); ds.setUser("APP") ds.setPassword("APP"); Connection con = ds.getConnection();
Beispieltabelle COFFEES COF_NAME Colombian
SUP_ID
PRICE
SALES
TOTAL
101
01.07.99
0
0
49
01.08.99
0
0
Espresso
150
01.09.99
0
0
Colombian_Decaf
101
01.08.99
0
0
49
01.09.99
0
0
French_Roast
French_Roast_Decaf
Connection Connection stellt die Verbindung zur Datenbank dar Kann über Statement-Objekte SQL Code ausführen Ergebnisse der Statements werden durch ResultSets repräsentiert
Daten aus Tabelle lesen Statement stmt = con.createStatement(); ResultSet srs = stmt.executeQuery("SELECT COF_NAME, PRICE FROM COFFEES"); ResultSet repräsentiert zurückgegebene Tabelle Methoden zum Auswählen der Zeile Methoden zum Auslesen einer Zelle zur aktuellen Zeile -> API
ResultSet Beispiel ResultSet srs = stmt.executeQuery( "SELECT COF_NAME, PRICE FROM COFFEES"); while (srs.next()) { String name = srs.getString("COF_NAME"); float price = srs.getFloat("PRICE"); System.out.println(name + " }
" + price);
Daten in der Tabelle ändern Statement stmt = conn.createStatement(); ResultSet srs = stmt.executeQuery("select COF_Name from COFFEES where price = 7.99"); srs.next(); srs.updateString("COF_NAME", "Foldgers"); srs.updateRow();
Änderungen werden erst mit updateRow() geschrieben Abbrechen mit cancelUpdates()
Weitere Features Daten einfügen und löschen INSERT/DELETE Statements
Transaktionen Es werden entweder alle Änderungen gemacht oder keine
Stored Procedures Werden in der Datenbank ausgeführt
Vor- und Nachteile Vorteile: Direkter und vollständiger Zugriff auf Daten Ausführung von beliebigem (nativem) SQL-Code Geringer Overhead
Nachteile:
Sehr aufwändig bei großen Datenbanken Duplikation von ähnlichen SQL-Statements Gefahr von Fehlern durch „copy & paste“ Kein direkter Bezug zu Objektmodell im Programm Kompatibilitätsprobleme bei Verwendung von RDBMS spezifischem Code
ORM – Objekt-Relationales Mapping Teil III
Persistente Datenspeicherung Programme arbeiten oftmals auf großen Datenbeständen Daten werden gelesen, geändert und gespeichert
Persistenz = Verwaltung und Speicherung von Daten über die Laufzeit eines Programms hinaus
Layerstruktur objektorientierter Applikationen
Objektorientierte Programmierung vs. Relationale Datenbanken Objektorientierte Programmierung fordert Einheit von Code und Daten Relationale Datenbank (RDB) beinhaltet nur Verwaltung von Daten und Aufrechterhaltung ihrer Konsistenz Relationale Datenbanken sind nicht in der Lage komplette Objekte (als Summe ihrer Attribute und Methoden) zu verwalten RDB eignet sich nur zur Speicherung der „Daten“ eines Objektes, also des Inhalts seiner Attribute Ansatz: Mapping, also das Abbilden von Objekten und Attributen in Datenbank-Tabellen bzw. deren Spalten
Persistenz-Layer Aufgabe: Bereitstellen von Methoden zum Erzeugen, Abfragen, Verändern und Löschen von Objekten einer Klassenhierarchie CRUD: Create, Retrieve, Update, Delete getter/setter-Methoden zur Abfrage und Veränderung einzelner Attribute Schlüssel zur Flexibilität und Geschwindigkeit ist die Art und Weise, mit der die Struktur der Objekte auf die relationale Datenbank gemappt werden Umfasst auch Information über Vererbungshierarchie
Identifizierung von Objekten in Datenbanken Ein Objekt existiert laut dem Paradigma der ObjektOrientierten Programmierung (OOP) nur einmal Jede Objekt-Orientierte Programmiersprache verwendet Referenzen auf Objekte, um diese zu verwalten: Objekt wird erzeugt, eine Referenz verwaltet, weitere Referenzen angelegt, Methoden werden benutzt und das Objekt wird am Ende seiner Existenz zerstört Gleichheit von Objekten wird durch Gleichheit der Referenz ausgedrückt Inhaltliche Gleichheit durch Gleichheit der Werte von Attributen
Identifizierung von Objekten in Datenbanken Beim Ablegen eines Objektes in einer relationalen Datenbank muss diese Form des „Selbst“ aufrecht erhalten werden Objekt muss eindeutig von anderen Objekten unterscheidbar und identifizierbar sein Persistenz-Layer weisen Objekten eindeutige Identifikationsnummern, genannt object ids (OID) zu Anhand dieser Nummer kann das Objekt sich selbst, seine Daten und assoziierte Objekte identifizieren
Identifizierung von Objekten anhand einfacher Nummern Fortlaufende Nummerierung der Objekte Datenbank-Systeme liefern hierzu geeignete Hilfsmittel, z.B. SERIAL INTEGER in PostgreSQL oder AUTOINCREMENT in MySQL Eigenhändige Lösung: Anlegen einer Datenbanktabelle für Metadaten zur Verwaltung der OIDs und anderer Daten Vorteil: Gute Übertragbarkeit auf jedes Datenbank-System Nachteil: Zusätzlicher Aufwand zur Verwaltung der Metadaten Um Eindeutigkeit der OID zu gewährleisten, muss jeder Zugriff auf die Metadaten exklusiv erfolgen Locking der Tabelle oder einzelner Spalten nicht zu umgehen Ausbremsen des Systems durch Locking, wenn viele Clients in kurzer Folge neue OIDs anfordern
Identifizierung von Objekten mit zusammengesetzten Nummern Um Performanz-Einbußen beim konkurrierenden Zugriff auf Metadaten zu verringern, wird OID aus zwei Komponenten zusammengesetzt (high-low-Ansatz) Zwei Komponenten: Sitzungszähler wird von Datenbank verwaltet und beim Starten der Applikation durch Datenbank initialisiert Zweite Komponente wird durch Applikation selbst vergeben
Beispiel: 32-bit-Integers (z.B. unterste 10 Bit aus internem Zähler, restliche Bits aus Sitzungszähler) gewährleistet Eindeutigkeit bei Verringerung der Zugriffe auf Metadaten um Faktor 210 = 1024 Nachteil: Verschwenderischer Umgang mit OIDs (Überlauf!)
Identifizierung von Objekten mit globalen OIDs Einsatz (weltweit) verteilter Datenbanken erfordert andere Mittel OID dient hier nicht nur dem Auffinden der Daten in der Datenbank, sondern muss es der Applikation auch ermöglichen, Datenbank als solche zu identifizieren Ansatz: Text-OIDs, die Hostnamen des DatenbankServers, Namen der Datenbank, Namen der Tabelle und numerische OID enthalten Andere Möglichkeiten: URIs und URNs
Mapping: Eine Tabelle pro Hierarchie Alle Klassen einer Hierarchie sowie ihre Attribute werden in eine einzige Tabelle gemappt Vorteile:
Hohe Performanz (nur eine Tabelle pro Anfrage) Einfache Implementierung Einfache Abfrage der Daten Nachträgliches Verändern der Klassen sowie polymorphe Abfragen von Objekten ebenfalls einfach
Nachteile: Je nach verwendeter Klassenstruktur hoher Speicherverbrauch, da unnötig viele Spalten pro Klasse existieren Viele leere Datenbankfelder
Mapping: Eine Tabelle pro konkreter Klasse Pro Klasse eine Tabelle, die die kompletten Attribute dieser Klasse (inklusive der Attribute evtl. vorhandener abstrakter Oberklassen) enthält Vorteile: einfache Implementierung Speicherbedarf hält sich in Grenzen
Nachteile: Polymorphe Abfragen von Objekten, besonders auf höherer Ebene der Klassenhierarchie, sind schwierig, da mehrere Tabellen nacheinander abgefragt werden müssen Veränderung von Attributen (Hinzufügen, Löschen) ist um so aufwändiger, je höher die Klasse in der Hierarchie angesiedelt ist
Mapping: Eine Tabelle pro Klasse Pro Klasse wird eine Tabelle erzeugt, die jedoch nur die in dieser Klasse definierten Attribute enthält, sowie die OID des Objektes Verknüpfung von Tabellen mit Hilfe der OID um die Gesamtdaten der Attribute eines Objektes zu erhalten Vorteile: Beste Unterstützung für polymorphe Abfragen
Nachteile: Höchste Anforderungen an Implementierung Performanz kann je nach verwendeter Datenbank geringer als bei anderen Methoden sein, da die Verknüpfung evtl. mehrerer Tabellen einen hohen Verarbeitungsaufwand erzeugt
Mapping: Ein einfaches Beispiel
Vor- und Nachteile der MappingVerfahren
Methode
Geschwindigkeit
Polymorphismus
Speicherbedarf
Hierarchie
+
-
-
Konkrete Klasse
+
-
+
Klasse
-
+
+
Datenspeicherung mit Hibernate Teil IV
Was ist Hibernate?
Englisch für „Winterschlaf halten“ Persistenz- und Objekt-Relationales Mapping-Framework für Java (mittlerweile auch für .NET)
Speicherung von Objekten mit Attributen und Methoden in relationalen Datenbanken Zugriff auf Daten mit eigener Abfragesprache (HQL) -
Dadurch: Unabhängig von gewählter Datenbank (z.B. MySQL)
Kompatibel zur Java Persistence API (JPA)
Open-Source
Verbreitung und Verwendung
Hibernate findet Verwendung in zehntausenden JavaProjekten weltweit
Etwa 25.000 angemeldete Entwickler in den Hibernate-Foren
Anzahl täglicher Download: ca. 3.000
(Quelle: Wikipedia 2010, Hibernate Framework)
POJO – Plain old Java Object = ein „ganz normales“ Objekt
Beispiel – Die Klasse / das POJO „Region“:
public class Region() { private String name; private int start; private int stop; public int getStart () { return start; } public int getStop () { return stop; } public String getName () { return name; } }
Primärschlüssel
Jedes Objekt benötigt zur eindeutigen Identifikation in der Datenbank einen eindeutigen Primärschlüssel Beispiel – Erweiterung der Klasse „Region“ um ein neues Attribut „_id“
public class Region() private Long _id;
{
public long get_id() { return _id; } }
Alternativ hätte auch das Attribut „name“ verwendet werden können → Dann müsste Eindeutigkeit aber sichergestellt sein!
Datenbanktabelle
Beispiel: Region
Feld
_id
name
start
stop
Typ
int(11)
char(255)
int(8)
int(8)
1
cg0025
1024
1258
2
cg0026
2080
1430
...
...
...
...
Objekt-Relationales Mapping
Erlaubt Abbildung der Objekt/Klassenstruktur auf ein relationales Datenbankschema Grundgerüst des Mappings in Hibernate mittels XML:
...
Alternativ möglich: Beschreibung des Mappings mit Hilfe von Annotationen
Objekt-Relationales Mapping
Mapping für die Klasse Region auf die entsprechende Datenbanktabelle:
...
Objekt-Relationales Mapping
Mapping für die Klasse Region auf die entsprechende Datenbanktabelle:
Objekt-Relationales Mapping
Wichtig: Definition des Primärschlüssels mit der Anweisung diesen bei jedem neuen Objekt automatisch zu erhöhen
Erweiterung der Klasse Region
Jede Region kann in einer Art „Eltern-Kind“-Relation zu einer anderen Region stehen:
public class Region() { ... private Region parentRegion; ... public Region getParentRegion () { return parentRegion; } }
Datenbanktabelle
Beispiel: Region erweitert um „Eltern (Parent)“ Region
Benötigt zusätzliche Spalte mit „Fremdschlüssel“
Feld
_id
name
start
stop
parent_region_id
Typ
int(11)
char(255)
int(8)
int(8)
int(11)
1
cg0025
1024
1258
2
2
cg0026
2080
1430
null
...
...
...
...
...
Objekt-Relationales Mapping
Fremdschlüssel zeigt auf „Parent“-Region
Datenspeicherung und Abfrage Transaktionen
Ausführung einer oder mehrerer Operationen (z.B. Holen und Änderung von Region-Objekten) innerhalb einer Transaktion ACID Eigenschaften von Transaktionen: Atomarität: „Ganz oder gar nicht“ Konsistenz: konsistenter Datenzustand (z.B. Abbuchung von Konto und Einzahlung auf einem anderen) Isolation: Keine gegenseitige Beeinflussung von Transaktionen (z.B. Reisebuchung – Sperrung eines Flugs während Auswahl des Hotels) Dauerhaftigkeit: Änderungen einer erfolgreich abgeschlossenen Transaktion sind dauerhaft (persistent)
Datenspeicherung und Abfrage
Komponenten/Architektur von Hibernate:
Session -
Repräsentiert „Konversation“ zwischen Anwender (Anwendung) und Datenbank
-
Kapselt Verbindung zur Datenbank (JDBC-Connection)
SessionFactory -
„Kennt“ alle Mappings für eine Datenbank
-
Dient als Factory für Session-Objekte Optional: Zwischenspeicher für abgerufene Datenbankobjekte zur Steigerung der Performance (Caching)
-
Hibernate Konfiguration
XML-Konfiguration einer SessionFactory
com.mysql.jdbc.Driver org.hibernate.dialect.MySQLDialect ...
Beispiel: Ablauf einer Datenbankabfrage
Erstellung der Verbindung zur Datenbank (Initialisierung der SessionFactory mit Hibernate Konfiguration)
Öffnen einer Session über die SessionFactory
Beginn einer Transaktion
Abfrage/Speicherung von Objekten
„Commit“ der Transaktion (Erst jetzt werden Änderung/ Objekte persistent!)
Schließen der Session
Später eventuell: Öffnen einer weiteren Session...
Beispiel: Holen aller „Regions“ // Initialisierung der SessionFactory (hier nicht gezeigt) private static SessionFacotory sessionFactory = ...
// Methode zum Holen aller Region-Objekte (Wo ist hier die Transaktion?) public List getRegions() { List regions = null; Session session = sessionFactory.openSession(); try { regions = session.createQuery( "select region from Region as region" ).list(); } catch (Exception ex) { // Fehlerbehandlung } finally { session.close(); } return regions; }
Beispiel: Speicherung einer Region // Methode zur Erstellung und Speicherung eines Region-Objekts public Region createNewRegion(String name, int start, int stop) { Region region = new Region(); region.setName(name); region.setStart(start); region.setStop(stop); Session session = sessionFactory.openSession(); Transaction tx = null; try { tx = session.beginTransaction(); session.save(region); tx.commit(); } catch (Exception ex) { tx.rollback(); // Rollback aller Änderungen! return null; // besser: Ausnahme werfen! } finally { session.close(); } return region; }
Literatur C. J. Date und H. Darwen SQL - Der Standard. Addison-Wesley, 1997 S.W. Ambler The Design of a Robust Persistence Layer for Relational Databases. S.W. Ambler Mapping Objects to Relational Databases. S.W. Ambler Building Object Applications that work. Cambridge University Press, 1998 http://www.agiledata.org/essays/mappingObjects.html http://download.oracle.com/docs/cd/E17409_01/javase/tutorial/jdbc/basics/index.html http://docs.jboss.org/hibernate/core/3.3/reference/en/html/tutorial.html C. Bauer, G. King (2005) Hibernate in Action. Manning Publications Co. Hibernate - JBoss Community (2010) www.hibernate.org
Vielen Dank für Eure Aufmerksamkeit