Folienset 17: Datenspeicherung

Werbung
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 <Tabellenname> "
(<Spaltenname> [, weitere Spaltennamen])
VALUES (<Wert für die erste Spalte> [, weitere Werte])"
Beispiel:
INSERT INTO Nutzer (Nutzer-ID, Vorname, Nachname) "
VALUES (13, 'Hans', 'Müller')"
Daten abfragen
Syntax:
SELECT ... FROM ... WHERE <Bedingung 1> "
[<logischer Operator> <Bedingung 2>]"
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 <Tabelle> SET <Name einer Spalte> = <Ausdruck aus
Spalten, Konstanten, Funktionen> [, weitere Spaltennamen = Ausdruck] WHERE <Bedingung>"
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 <Tabelle> WHERE <Bedingung>"
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:<treiber>:<dbname>[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:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernatemapping-3.0.dtd">
<hibernate-mapping>
...
</hibernate-mapping>
 
Alternativ möglich: Beschreibung des Mappings mit Hilfe von
Annotationen
Objekt-Relationales Mapping
 
Mapping für die Klasse Region auf die entsprechende
Datenbanktabelle:
<hibernate-mapping>
<class name="de.cebitec.Region" table="Region">
...
</class>
</hibernate-mapping>
Objekt-Relationales Mapping
 
Mapping für die Klasse Region auf die entsprechende
Datenbanktabelle:
<hibernate-mapping>
<class name="de.cebitec.Region" table="Region">
<property name="start" column="start"/>
<property name="stop" column="stop"/>
<property name="name" column="name"/>
</class>
</hibernate-mapping>
Objekt-Relationales Mapping
 
Wichtig: Definition des Primärschlüssels mit der Anweisung
diesen bei jedem neuen Objekt automatisch zu erhöhen
<hibernate-mapping>
<class name="de.cebitec.Region" table="Region">
<property name="start" column="start"/>
<property name="stop" column="stop"/>
<property name="name" column="name"/>
<id column="_id" name="_id">
<generator class="increment"/>
</id>
</class>
</hibernate-mapping>
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
<hibernate-mapping>
<class name="ABC.Region" table="Region">
<id column="_id" name="_id">
<generator class="native"/>
</id>
<property name="start" column="start"/>
<property name="stop" column="stop"/>
<property name="name" column="name"/>
<many-to-one class="de.cebitec.Region"
column="parent_region_id"
name="parent_region"/>
</class>
</hibernate-mapping>
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
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernateconfiguration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="dialect">
org.hibernate.dialect.MySQLDialect
</property>
...
<mapping resource="de/cebitec/Region.hbm.xml"/>
</session-factory>
</hibernate-configuration>
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<Region> getRegions() {
List<Region> 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
Herunterladen