Vorlesungsscript DBprog - FH - Geändert

Werbung
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) David Schiffer
WS 2007/2008
2.Auflage (2007)
[email protected]
http://fh.atbits.de
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Inhalt
1
2
3
4
5
6
Allgemein..........................................................................................................................5
1.1
Überblick...................................................................................................................5
1.1.1
Zugriff auf Datenbanken: ...................................................................................5
1.1.2
Zusatzprogramme innerhalb von Datenbanken: ................................................6
1.2
Motivation für Datenbankprogrammierung ................................................................6
1.3
Beziehungen zu anderen Fächern ............................................................................7
1.4
Bestandteile einer DB-Anwendung............................................................................8
1.5
Datenbank Architekturen...........................................................................................9
1.6
Fragestellungen bei Datenbankprogrammierung.....................................................11
SQL – eine Wiederholung?.............................................................................................12
Probleme bei Datenbankprogrammierung ......................................................................22
3.1
Die Coddschen Regeln ...........................................................................................22
3.2
Der „Impedance Mismatch“ .....................................................................................24
3.2.1
Was ist der Impedance Mismatch?..................................................................24
3.2.2
Objekte versus Tabellen..................................................................................25
3.2.3
Die unterschiedlichen Paradigmen in der Praxis .............................................26
3.3
Cursor-Konzept.......................................................................................................27
3.4
Andere Lösungsansätze zur Überwindung des Impedance Mismatch.....................28
Zugriff auf Datenbanken .................................................................................................29
4.1
Allgemein ................................................................................................................29
4.2
Verarbeitung einer SQL-Anfrage.............................................................................30
Datenbankprogrammierung innerhalb der Datenbank - Stored Procedures & Trigger.....31
5.1
Was sind Stored Procedures?.................................................................................31
5.2
Wozu dienen Stored Procedures? ..........................................................................31
5.3
Wer bietet Stored Procedures? ...............................................................................31
5.4
Wann sollten Stored Procedures verwendet werden?.............................................32
5.5
Stored Procedures / Trigger (bei Oracle) ................................................................32
5.5.1
Allgemein ........................................................................................................32
5.5.2
Aufbau von Stored Procedures........................................................................33
5.5.3
Beispiel (PL/SQL)............................................................................................33
5.5.4
Ausnahmebehandlung ....................................................................................34
5.5.5
Aufbau von Triggern (PL/SQL) ........................................................................35
5.5.6
Beispiel (PL/SQL)............................................................................................35
5.6
Erstellen von Stored Procedures.............................................................................36
5.7
Aufruf und Ablauf von Stored Procedures ...............................................................37
5.7.1
Aufruf einer Stored Procedure aus dem Client-Programm...............................37
5.7.2
Stored Procedure auf dem DB-Server .............................................................37
Java Database Connectivity (JDBC)...............................................................................38
6.1
Allgemein ................................................................................................................38
6.2
Grundstruktur eines JDBC-Programms...................................................................38
6.3
Einbinden der JDBC-Klassen..................................................................................39
6.4
Architektur...............................................................................................................40
6.5
Verbindungsaufbau zur Datenbank .........................................................................42
6.5.1
Laden eines JDBC Treibers ............................................................................44
6.5.2
Verbindung zur Datenbank: Connection Objekt erzeugen ...............................44
6.6
Datenzugriff mittels SQL .........................................................................................47
6.6.1
Dynamisches SQL mit Statments ....................................................................47
6.6.2
Vorkompiliertes SQL .......................................................................................48
6.7
Ergebnisverarbeitung mittels ResultSet ..................................................................51
6.7.1
ResultSets.......................................................................................................51
6.7.2
Ergebnisabfrage..............................................................................................52
6.7.3
Datensätze ändern..........................................................................................52
6.7.4
Datensätze einfügen .......................................................................................53
6.7.5
Batch Updates.................................................................................................54
Seite 2 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Fehlerbehandlung ...................................................................................................56
6.8
6.8.1
Allgemein ........................................................................................................56
6.8.2
Abfangen von Fehlern und Ausnahmen ..........................................................58
6.8.3
Abfragen von Warnungen................................................................................59
6.9
Verbindungsabbau..................................................................................................60
6.10 Transaktionssteuerung............................................................................................60
6.11 Fazit........................................................................................................................61
6.12 Erweiterte Themen..................................................................................................62
6.12.1
JNDI & Connection Pooling .............................................................................62
7
DAPRO - Best Practices.................................................................................................64
7.1
DAO – Data Access Objects ...................................................................................65
7.1.1
Allgemein: .......................................................................................................66
7.1.2
Am Beispiel: Kundeninformation......................................................................68
7.1.3
Vorteile............................................................................................................71
8
Embedded SQL ..............................................................................................................73
8.1
Allgemein ................................................................................................................73
8.2
Beispiel ...................................................................................................................73
8.3
Übersetzung eines Programms mit embedded SQL ...............................................74
8.4
Datenaustausch zwischen Programm und Datenbank ............................................74
8.5
Cursorkonzept ........................................................................................................75
8.6
Statisches und dynamisches SQL...........................................................................76
8.7
Kopplung zwischen Anwendung und Datenbank.....................................................77
8.8
Fazit........................................................................................................................77
9
Open Database Connectivity (ODBC).............................................................................78
9.1
Allgemein ................................................................................................................78
9.1.1
Unterschiede zu embedded SQL.....................................................................78
9.1.2
Ziele ................................................................................................................78
9.1.3
Design-Prinzipien ............................................................................................78
9.2
Architektur...............................................................................................................79
9.3
Vergleich zu proprietären DB-API’s.........................................................................80
9.4
Programmschritte beim ODBC-Zugriff.....................................................................81
9.5
Beispiel ...................................................................................................................82
9.6
Prinzipien von ODBC ..............................................................................................86
9.6.1
Handles...........................................................................................................86
9.6.2
Fehlerbehandlung ...........................................................................................86
9.7
Dynamisches SQL in ODBC ...................................................................................87
9.8
Retrieval von Informationen in ODBC .....................................................................87
9.9
Fazit........................................................................................................................88
10
ActiveX Data Objects.NET (ADO.NET) .......................................................................89
10.1 Allgemein ................................................................................................................89
10.2 Historie ...................................................................................................................90
10.2.1
Open Database Connectivity (ODBC) ..............................................................90
10.2.2
Data Access Objects (DAO) ............................................................................90
10.2.3
Remote Data Objects (RDO)...........................................................................91
10.2.4
OLE DB...........................................................................................................91
10.2.5
ADO - ActiveX Data Objects............................................................................92
10.3 Das ADO.NET Objekt Modell ..................................................................................93
10.3.1
Managed Provider ...........................................................................................95
10.4 Connection Objekt ..................................................................................................96
10.5 Command Objekt ....................................................................................................96
10.6 DataReader Objekt .................................................................................................98
10.7 DataAdapter..........................................................................................................100
10.8 DataSet Objekt .....................................................................................................101
10.9 DataTable .............................................................................................................102
10.10
DataRelation .....................................................................................................103
10.11
XML ..................................................................................................................104
Seite 3 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Fazit ..................................................................................................................104
10.12
11
Literatur: ...................................................................................................................105
Seite 4 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
1 Allgemein
1.1 Überblick
Die Vorlesung Datenbankprogrammierung beschäftigt sich mit:
1.1.1 Zugriff auf Datenbanken:
•
JDBC
•
•
•
•
Embedded SQL
ODBC
ADO
Proprietäre Datenbankschnittstellen (z.B. OCI)
Ausser bei Embedded SQL handelt es sich bei allen Datenbank-API’s um sog. Call Level
Interfaces (CLI).
CLI bei Wikipedia:
Das Call Level Interface (kurz CLI) ist eine Datenbankschnittstellen-Spezifikation für den
Zugriff auf relationale Datenbanken aus anderen Anwendungen heraus.
Bei konkreten Implementierungen des CLI handelt es sich um so genannte Middleware, mit
deren Hilfe aus beliebigen Anwendungen auf beliebige Datenbanken zugegriffen werden
kann. Ähnlich wie Gerätetreiber zum Ansteuern der Hardware wird die CLI-Implementierung
zum Ansteuern von Datenbanken benötigt. Somit muss der Programmierer einer Anwendung
den Funktionsumfang zum Ansprechen der Datenbank nicht für jedes Programm neu
schreiben, sondern greift auf bestehende Module zurück. Die CLI-Implementierung bildet
ebenfalls die Befehlssätze verschiedener Datenbanksysteme auf eine immer gleiche
Funktionsbibliothek ab, so dass es für das Programm an sich unwichtig ist, mit welcher
Datenbank es arbeitet. Das CLI hat also die Funktion eines Übersetzers, der
Programmaufrufe in eine „Sprache“ übersetzt, die die Datenbank versteht und umgekehrt
Daten, die von der Datenbank zurückgeliefert werden, so formatiert, dass sie vom Programm
verarbeitet werden können.
Bekannte Programmierschnittstellen, die das CLI-Konzept umsetzen, sind Open Database
Connectivity (ODBC) und Java Database Connectivity (JDBC).
Der CLI-Standard wurde Anfang 1990 von einem Konsortium aus Datenbankherstellern – der
SQL Access Group – entwickelt und 1992 mit Microsofts ODBC erstmals umgesetzt. Die ISOSeite 5 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
und ANSI-Standardisierung folgte 1993. Seit Ende 1994 wird der CLI-Standard von der
X/Open-Gruppe weiterentwickelt, welche selbst im Konsortium The Open Group aufging.
1.1.2 Zusatzprogramme innerhalb von Datenbanken:
•
•
•
Stored Procedures
Stored Functions
Trigger
1.2 Motivation für Datenbankprogrammierung
Jede moderne eBusiness-Applikation basiert in ihrer Architektur auf Datenbanken.
Die Erstellung von Business Software verlangt also nach Datenbankprogrammierung.
Seite 6 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
1.3 Beziehungen zu anderen Fächern
Programmieren
Datenbanksysteme
Objektorientierte
Programmierung
Client-Server
Datenbankprogrammierung
Internet 2
Objektorientierte
Datenbanken
Seite 7 von 104
Middleware
eBusiness
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
1.4 Bestandteile einer DB-Anwendung
Bei der Entwicklung von datenbankgestützten Anwendungen kommt in der Regel ein
Schichten-Modell zum Einsatz.
Typische Schichten darin sind:
Die Benutzerschnittstelle. Die Schichten, die die Anwendung dem Benutzer präsentiert.
Die Anwendungs- / Geschäftslogik. Die Software, die die Datenbank für den Benutzer
verfügbar macht, indem sie Funktionen der Datenbank-Schnittstelle verwendet, so
etwa Daten selektiert oder manipuliert und sie über die Benutzerschnittstelle
verfügbar macht.
Die Datenbank-Schnittstelle. Die Komponente, mittels der die Anwendung mit dem
Datenbank-Management-System interagiert. Diese Komponente wird vom Entwickler
der Anwendung verwendet und auf diese konzentriert sich das darauf folgende
Kapitel.
Die Vermittlungssoftware. Hierbei handelt es sich z.B. um Betriebssystem-, Netzwerk-, und
Kommunikationssoftware, die (je nach ‚Architektur) die Verbindung zwischen der
Datenbank-Schnittstelle und der Datenbank herstellt.
Das Datenbank-Management-System. Die Software, die die eigentlichen Datenzugriffe auf
dem Datenspeicher durchführt. Wenn wir mit einer SQL-Datenbank arbeiten, enthält
diese Komponente einen SQL-Parser, einen Optimierer, ein Datenzugriffsmodul, etc.
Kurz ausgedrückt stellt diese Komponente die „Datenbank“ oder den „Server“ dar.
Der Datenspeicher. Die Komponente, die die eigentlichen Datenbank-Inhalte gespeichert
hält. Dies ist in der Regel ein Festplatten-System.
Seite 8 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
1.5 Datenbank Architekturen
Ausgehend von dem Schichten-Modell sollten wir betrachten, in welcher Art und Weise die
Komponenten implementiert sind bzw. miteinander agieren. Diese Betrachtung gibt uns einen
groben Überblick über die gängigen Datenbank-Architekturen.
Im Folgenden teilen wir diese Architekturen in 4 Gruppen ein:
1. Zentralisierte Datenbank-Systeme, die in der Regel auf Mainframes laufen. Die
Datenbank-Anwendung läuft als Prozess auf dem Server, die Schnittstelle zum
Anwender ist nur ein einfaches Terminal. Dabei wird selbstverständlich davon
ausgegangen, dass die Anwendung ein vom eigentlichen DatenbankManagementsystem getrennter Prozess ist, der durch einen Prozess-KommunikationsMechanismus des Betriebssystems mit dem Datenbank-Prozess kommuniziert.
2. Client-Server-Datenbanken verwenden einen zentralen Datenbank-Server. Die
Anwendung selbst jedoch läuft im Gegensatz zu den zentralisierten DatenbankSystemen auf dem Client. Die Anwendung ist von der Datenbank getrennt und
kommuniziert mit dem Datenbank-Server über eine Vermittlungssoftware, die in der
Regel ein Daten-Protokoll zwischen Client und Server betreibt.
3. Desktop-Datenbanken sind in der Regel auf einem einzelnen PC installiert. Die
Datenbankanwwendung verwendet eine Programmbibliothek (z.B. ISAM-Bibliothek)
um auf die Daten zuzugreifen.
Mittlerweile setzen sich aber auch in diesem Bereich verstärkt SQL-Datenbanken
durch. In diesem Fall stellt sich eine ähnliche Konstellation wie bei den Client-ServerDatenbank dar, wobei Client und Server auf einem Rechner betrieben werden.
4. 3-Tier-Architektur unterscheidet sich von der Client-Server-Architektur darin, dass
zwischen Client und Server eine zusätzliche Schicht eingeführt wird. Diese zusätzliche
Schicht enthält die Anwendungslogik. Auf diese Weise entsteht ein Applikationsserver.
Die Archtitektur kommt insbesondere bei web-basierten Anwendungen häufig zum
Einsatz. Der Client selbst führt keinen Datenbank-Zugriff durch.
Seite 9 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Seite 10 von 104
Dipl.-Ing. (FH) D. Schiffer
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
1.6 Fragestellungen bei Datenbankprogrammierung
Das Client-Server-Modell deckt eine sehr große Bandbreite der Datenbank-Architekturen ab.
Zur näheren Erläuterung der Datenbank-Zugriffe ergeben sich diverse konzeptionelle Fragen:
1. Wie verwendet das Anwendungsprogramm die Funktionalität der Datenbank?
Welche Rolle spielt dabei die Programmiersprache des Anwendungsprogramms?
Durch welche Mechanismen löst der Prozess des Anwendungsprogramms eine Aktion
des Datenbank-Servers aus?
2. Wie findet der Datenaustausch zwischen Anwendung und Datenbank statt?
Z.B. ein Suchkriterium, das der Datenbank mitgeteilt wird, oder ein Ergebnisfeld, das
die Datenbank der Anwendung liefert. Wie werden die Daten ausgetauscht? Welche
Typumwandlungen finden statt? Wie werden NULL-Werte der Datenbank im
Datenaustausch behandelt? Wie erfährt die Anwendung von Fehlern und Warnungen
des Datenbank-Prozesses?
3. Wie wird eine Ergebnismenge der Datenbank an die Anwendung übergeben?
Eine Datenbank kann eine (u.U. sehr große) Ergebnismenge liefern.
Programmiersprachen, wie die des Anwendungsprogramms haben in der Regel kein
Mengenkonzept.
4. Zu welchem Zeitpunkt findet welche Aktion der Datenbank statt?
Welche Reihenfolge von Aktionen erwartet die Datenbank bzw. wie sorgt die
Schnittstelle für die Einhaltung des geforderten Vorgehens?
5. Wie kann man voll dynamische Abfragen an die Datenbank richten?
Oftmals werden Anwendungen entwickelt, in denen Abfragen vorkommen, bei denen
zum Zeitpunkt der Erstellung der Anwendung noch gar nicht feststeht, wie die
Ergebnismenge aufgebaut ist.
6. Welche Art von Kopplung entsteht zwischen Anwendungsprogramm und
Datenbank?
Was passiert, wenn man das Datenbank-System austauscht? Was passiert, wenn
man die Datenbank-Struktur ändert: veränderte Spalten, zusätzliche Spalten? Was
passiert, wenn sich das Anwendungsprogramm ändert, etwa die Portierung auf eine
andere Programmiersprache?
Im Folgenden Kapitel wird auf diverse Datenbank-APIs eingegangen und fallweise auf diese
Fragestellungen eingegangen.
Seite 11 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
2 SQL – eine Wiederholung?
So genannte Datenbank-Abfragen zur Ermittlung von Datenbankinhalten stellen den
elementaren Sprachkern von SQL dar, mit der größten Bedeutung.
Jede Abfrage wird durch das Schlüsselwort SELECT eingeleitet.
Syntax der SELECT-Anweisung:
SELECT [ALL|DISTINCT] select_item,...
FROM table_specification,...
[WHERE search_condition]
[GROUP BY grouping_column,...]
[HAVING search_condition]
[ORDER BY sort_specification,...]
Als weiterführende Literatur sei hier auf [1] verwiesen.
Das Erstellen einfachster Abfrageformen wird an dieser Stelle vorausgesetzt!
Enthält die Ausgabe nicht den Primärschlüssel einer Tabelle, so kann es vorkommen, das
mehrfach dieselben Werte ausgegeben werden. Dies kann durch Angabe des
Schlüsselwortes DISTINCT in der SELECT-Klausel vermieden werden.
DISTINCT überschreibt das vorgegebene Verhalten (ALL) alle Einträge auszugeben.
SELECT DISTINCT LASTNAME
FROM EMPLOYEE;
liefert alle verschiedenen Werteinträge der Spalte LASTNAME duplikatfrei
Durch Angabe mehrerer Einträge in der FROM-Klausel können Inhalte aus verschiedenen
Tabellen innerhalb einer Anfrage extrahiert werden:
SELECT DNAME, PNUMBER
FROM DEPARTMENT, PROJECT;
Die Anfrage bildet das kartesische Produkt der beiden angefragten Tabellen.
Aliasbildung
Bei Anfragen über mehrere Tabellen kann es zu Konflikten hinsichtlich der Eindeutigkeit der
Spaltenbezeichner kommen. So könnte bspw. in 2 Tabellen die Spalte NAME vorkommen.
Einmal als NAME in der Tabelle AUTO und einmal als NAME in der Tabelle
FAHRZEUGHALTER
SELECT NAME, NAME
FROM AUTO, FAHRZEUGHALTER;
Als Lösung bietet SQL die Möglichkeit den Spaltennamen zusätzlich durch Voranstellung des
Namens der die Spalte beherbergenden Tabelle zu qualifizieren um die erforderliche
Eindeutigkeit herzustellen:
SELECT AUTO.NAME, FAHRZEUGHALTER.NAME
FROM AUTO, FAHRZEUGHALTER;
Durch Nutzung der Möglichkeit Alternativnamen für Tabellen, sog. Aliasnamen, anzugeben
Seite 12 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
ergibt sich eine in der Schreibung kompaktere Umsetzung:
SELECT t1.NAME, t2.NAME
FROM AUTO as t1, FAHRZEUGHALTER as t2;
Gleichzeitig kann die Aliasbildung verwendet werden, um die Benennung der Spalten bei der
Ausgabe zu modifizieren. Auf diesem Wege können wenig sprechende Namen oder
Doppelbenennungen umgangen werden.
SELECT t1.NAME as autoname, t2.NAME as haltername
FROM AUTO as t1, FAHRZEUGHALTER as t2;
Beschränkung der Ergebnismenge
Die bisher betrachteten Anfrageformen lieferten immer die gesamten Inhalte der betrachteten
Tabellen. Durch Angabe einer einschränkenden Bedingung innerhalb der WHERE-Klausel
einer SELECT-Anweisung können die Tabelleninhalte hinsichtlich einer Bedingung gefiltert
werden.
SELECT NAME, BIRTHDAY
FROM FAHRZEUGHALTER
WHERE NAME="HUGO";
Durch die Filterung der Ergebnistupel werden alle diejenigen Datenbankeinträge, welche die
getroffene Bedingung nicht erfüllen ausgeblendet. Dieser Vorgang wird als Selektion
bezeichnet.
Neben den einfachen Vergleichsoperationen können durch den LIKE-Operator unscharfe
musterbasierte Suchen ausgedrückt werden.
Die Musterausdrücke werden dabei aus den tatsächlich in der Ergebnisemenge erwarteten
Zeichen ergänzt um Metazeichen mit besonderer Bedeutung zusammengesetzt.
Hierbei stehen „%“ zur Stellvertretung einer (möglicherweise leeren) Menge beliebiger
Zeichen und „_“ zur Stellvertretung genau eines Zeichens zur Verfügung.
SELECT NAME, BIRTHDAY
FROM FAHRZEUGHALTER
WHERE NAME="H%";
Kombination von Einzelbedingungen
Zur Selektion nach mehreren Bedingungen können diese mit den logischen Operationen AND,
OR und NOT kombiniert werden.
SELECT NAME, BIRTHDAY
FROM FAHRZEUGHALTER
WHERE NAME="H%" AND SALARY > 50000;
Mittels der Verknüpfungsoperatoren können auch einige der zuvor gezeigten
Vergleichsoperatoren ausgedrückt werden.
Vergleichsoperator Alternative Schreibweise mit Bedingungsverknüpfung
a BETWEEN b and c
(a >= b) AND (a <= c)
x IN (a, b, c)
(x = a) OR (x = b) OR (x = c)
Seite 13 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
JOINS / Verbünde
Häufig besteht der Wunsch Werte aus verschiedenen Tabellen nicht nur gemeinsam
abzufragen und anzuzeigen, sondern auch inhaltlich in Beziehung zu setzen.
Im Beispiel wird versucht durch die Abfrage der Tabellen EMPLOYEE und DEPARTMENT die
Namen der Mitarbeiter mit der (in der anderen Tabelle abgelegte) Bezeichnung der Abteilung
verbunden.
Aufgrund der Bildung des kartesischen Produkts werden jedoch alle theoretisch möglichen
Kombinationen geliefert und nicht die Untermenge der tatsächlich existierenden Paarungen.
SELECT FNAME, DNAME
FROM DEPARTMENT, EMPLOYEE;
Innerer Verbund / Inner Join
Durch (geschickte) Nutzung der WHERE-Bedingung, die Werte aus beiden Tabellen
miteinander in Beziehung setzt, gelingt jedoch die gewünschte Ermittlung:
SELECT FNAME, DNAME
FROM DEPARTMENT AS d, EMPLOYEE as e
WHERE d.DNUMBER = e.DNO;
Diese Abfrage liefert lediglich diejenigen Tupel, für die das in EMPLOYEE abgespeicherte
DNO-Attribut einen Wert enthält, der auch in der Spalte DNUMBER der Tabelle
DEPARTMENT auftritt. Es handelt sich um einen sog. Inneren Verbund oder Inner-Join
Definition: Innerer Verbund
Ein Innerer Verbund enthält die selektierten Daten aller beteiligten Tabellen, welche die
formulierte Einschränkungsbedingung erfüllen.
Der SQL-Standard gibt für diese besondere Anfrageform eine eigene Syntax vor:
SELECT FNAME, DNAME
FROM DEPARTMENT AS d INNER JOIN EMPLOYEE AS e
ON d.DNUMBER = e.DNO;
Die Bildung von Verbünden ist nicht auf die Angabe verschiedener Tabellen beschränkt,
sondern kann auch durch mehrfache Bezugnahme auf dieselbe Tabelle geschehen:
SELECT e1.FNAME as Chef, e2.FNAME as Mitarbeiter
FROM EMPLOYEE AS e1, EMPLOYEE AS e2
WHERE e1.SSN = e2.SUPERSSN;
Selbstverständlich können Joins auch über mehrere Tabellen getätigt werden.
SELECT FNAME, DNAME, PNAME
FROM EMPLOYEE AS e, DEPARTMENT AS d, PROJECT AS p, WORKS_ON AS w
WHERE d.DNUMBER = e.DNO AND e.SSN = w.ESSN AND w.PNO = p.PNUMBER;
Die Anfrage liefert die Familiennamen (Tabelle EMPLOYEE) sowie die Abteilungen denen der
Mitarbeiter zugeordnet ist (aus Tabelle DEPARTMENT) sowie die durch den Mitarbeiter
bearbeiteten Projekte (Tabelle PROJECT).
Die Tabelle PROJECT kann jedoch nicht direkt in den Verbund einbezogen werden, da sie
über keine geeigneten Attribute (d.h. Attribute die mit derselben Semantik in einer der beiden
anderen Tabellen auftreten) verfügt. Daher wird zusätzlich die Tabelle WORKS_ON in die
Anfrage miteinbezogen, weil sie mit dem Attribut ESSN ein Attribut bietet, welches die in
Seite 14 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
EMPLOYEE enthaltene Attribut SSN als Fremdschlüssel beinhaltet. Ausgehend hiervon kann
eine Bedingung unter Einbezug von PROJECT formuliert werden.
Für Verbünde ist die Bildung durch ausschließliche Nutzung des Gleichheitsoperators
innerhalb der WHERE-Klausel keineswegs zwingend, wenngleich diese sog. Equi Joins eine
häufige Anwendungsform darstellen.
SELECT FNAME, DNAME
FROM EMPLOYEE JOIN DEPARTMENT
ON DEPARTMENT.DNUMBER < EMPLOYEE.DNO;
Das Beispiel zeigt einen durch Nutzung des kleiner-Operators gebildeten Verbund, der alle
Abteilungen enthält, in denen ein Mitarbeiter (noch) nicht arbeitet und deren
Abteilungsnummer größer ist als die Nummer der Abteilung welcher der Mitarbeiter
gegenwärtig zugeordnet ist. (Mögliche semantische Deutung: Liste möglicher Beförderungen,
sofern größere Abteilungsnummern einen Aufstieg codieren.
Äußere Verbunde
Neben der Möglichkeit durch innere Verbünde Tupel die über Attribute mit übereinstimmenden
Wertbelegungen zu selektieren besteht durch äußere Verbünde die Möglichkeit neben den
Tupeln mit übereinstimmenden Werten alle Tupel einer am Verbund beteiligten Tabelle
vollständig zu selektieren.
Definition: Äußerer Verbund
Ein Äußerer Verbund enthält die selektierten Daten aller beteiligten Tabellen, welche die
formulierte Einschränkungsbedingung erfüllen, sowie alle Daten der „äußeren“ Tabelle.
Die nicht mit Werten belegbaren Felder werden durch NULL aufgefüllt.
Konzeptionell wird zwischen linken und rechten Äußeren Verbünden unterschieden. Die
„Seite“ des Verbundes gibt diejenige beteiligte Tabelle an, die im Rahmen der Verbundbildung
vollständig ausgegeben wird.
Folgendes Beispiel zeigt einen linken äußeren Verbundes:
INSERT INTO EMPLOYEE VALUES("John", "X", "Doe", "999999999", "196503-04", "42 XYZ Street", "M", 50000, NULL, NULL);
SELECT FNAME, DNAME
FROM EMPLOYEE LEFT OUTER JOIN DEPARTMENT
ON DEPARTMENT.DNUMBER = EMPLOYEE.DNO;
Das Beispiel fügt zunächst einen Tupel zur Tabelle EMPLOYEE hinzu, der keiner Abteilung
zugeordnet ist. In einem Inneren Verbund erscheint dieser Tupel daher nicht. Der linke Äußere
Verbund des Beispiels hingegen umfaßt alle Tupel aus EMPLOYEE sowie die Werte der
hinsichtlich der Bedingung DEPARTMENT.DNUMBER = EMPLOYEE.DNO ermittelten
Übereinstimmungen in DEPARTMENT.
Für die nicht ermittelbaren Übereinstimmungen werden NULL-Werte erzeugt.
Dieses Beispiel veranschaulicht einen rechten äußeren Verbund:
INSERT INTO DEPARTMENT VALUES("New Dept.", 0, 888665555, NULL);
SELECT FNAME, DNAME
FROM EMPLOYEE RIGHT OUTER JOIN DEPARTMENT
ON DEPARTMENT.DNUMBER = EMPLOYEE.DNO;
Seite 15 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Auch hier wird zunächst einen Datensatz eingefügt; diesmal in die Tabelle DEPARTMENT,
der zu keinem Tupel in EMPLOYEE in Beziehung steht. Analog dem linken Äußeren Verbund
liefert der rechte Äußere Verbund alle Tupel der rechtsstehenden Tabelle (DEPARTMENT)
sowie die mit EMPLOYEE übereinstimmenden.
Kreuzverbund
Der Kreuzverbund liefert alle gemäß den Gesetzen des kartesischen Produkts bildbaren
Kombinationen aus Tupeln der beitragenden Relationen:
SELECT DNAME, PNUMBER
FROM DEPARTMENT CROSS JOIN PROJECT;
Das Ergebnis diesen Beispiels entspricht dem folgender Abfrage:
SELECT FNAME, DNAME
FROM DEPARTMENT, EMPLOYEE;
Wird beim Kreuzverbund eine Bedingung angegeben, so entspricht er dem Inneren Verbund:
SELECT FNAME, DNAME
FROM DEPARTMENT CROSS JOIN EMPLOYEE
WHERE DEPARTMENT.DNUMBER = EMPLOYEE.DNO;
Weitere Informationen zu Joins finden Sie unter
http://www.onjava.com/lpt/a/4443#1 oder
http://www.onjava.com/pub/a/onjava/2004/01/07/SQLJoins.html
Sortierungen
Zur Sortierung hinsichtlich einer oder mehrerer Spalten der als Anfrageergebnis ermittelten
Tabelle steht die ORDER BY-Klausel zur Verfügung.
CREATE TABLE Person(
Vorname VARCHAR(10),
Nachname VARCHAR(10));
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INSERT
INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO
INTO
Person
Person
Person
Person
Person
Person
Person
Person
Person
VALUES("Adam", "C-Mann");
VALUES("Cesar", "C-Mann");
VALUES("Berta", "C-Mann");
VALUES("Adam", "A-Mann");
VALUES("Cesar", "A-Mann");
VALUES("Berta", "A-Mann");
VALUES("Adam", "B-Mann");
VALUES("Cesar", "B-Mann");
VALUES("Berta", "B-Mann");
SELECT *
FROM Person
ORDER BY Nachname;
Ist die Sortierung bezüglich mehrerer Attribute, d.h. Sortierung innerhalb eines gleicher
Attributwerte hinsichtlich eines anderen Attributs, gewünscht, so können auch mehrere
Sortierattribute in der ORDER BY-Klausel versammelt werden.
Zusätzlich zeigt das Beispiel die Kurzschreibweise, welche die zu sortierenden Attribute nicht
namentlich benennt, sondern nur hinsichtlich ihrer Position innerhalb der SELECT-Klausel
referenziert.
Seite 16 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
SELECT *
FROM Person
ORDER BY Nachname, Vorname;
Vorgabegemäß erfolgt die Sortierung aufsteigend (ascending). Die Umkehrung der
Sortierreihenfolge kann durch nachstellen der Zeichenfolge DESC (für descending) nach dem
Namen des Sortierattributes erreicht werden.
Die aufsteigende Vorgabesortierung (ASC) wird üblicherweise nicht ausgeschrieben.
SELECT *
FROM Person
ORDER BY Nachname ASC, Vorname DESC;
Unteranfragen
Bisher wurden Anfragen lediglich auf Tabellen in ihrer Rolle als in der Datenbank abgelegte
Eingabemengen betrachtet. Die relationale Sichtweise erfordert jedoch keineswegs, dass die
Eingangswerte einer Anfrage direkt aus der Datenbank gelesen werden müssen. Sie können
auch Ergebnis einer weiteren Anfrage sein.
Anfragen die vor einer anderen Anfrage ausgeführt werden müssen um für diese
Eingangswerte zu liefern werden daher als Unterabfragen (subqueries, nested queries oder
einfach als Sub-Selects) bezeichnet.
Das folgende Beispiel zeigt eine solche Unteranfrage die alle Projektnummern liefert welche
Projekten zugeordnet sind die in der durch Smith geleiteten Abteilung bearbeitet werden.
Eine zweite Unterabfrage des Beispiels liefert alle Nummern von Projekten an denen dieser
Mitarbeiter selbst arbeitet.
Die durch diese Abfrage gelieferten Daten (Projektnummern) sind Eingangsdaten in die
Ermittlung der Projektnamen.
SELECT DISTINCT PNAME
FROM PROJECT
WHERE PNUMBER IN (
SELECT PNUMBER
FROM PROJECT AS p, DEPARTMENT AS d, EMPLOYEE AS e
WHERE e.SSN = d.MGRSSN AND
d.DNUMBER = p.DNUM AND e.LNAME="Smith"
)
OR
PNUMBER IN (
SELECT PNO
FROM WORKS_ON AS w, EMPLOYEE AS e
WHERE w.ESSN = e.SSN AND e.LNAME="Smith"
);
Das nächste Beispiel zeigt den Vergleich eines Einzelwertes (SALARY) mit einer Menge
gelieferter Werte. Die Anfrage ermittelt diejenigen Mitarbeiter, deren Einkommen höher liegt
als das Einkommen aller Mitarbeit in Abteilung Nummer 5. (Hinweis es wird nicht ermittelt ob
das Einkommen größer ist als die Summe aller Einkommen der Mitarbeiter aus Abteilung 5,
sondern nur ob das Einkommen größer ist als jedes Einzeleinkommen eines Mitarbeiters aus
Abteilung 5.)
SELECT LNAME, FNAME
FROM EMPLOYEE
WHERE SALARY > ALL (SELECT SALARY FROM EMPLOYEE WHERE DNO=5);
Seite 17 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Korrelierte Unteranfragen
Eine besondere Form der Unteranfragen stellen solche dar, die sich in ihrer WHERE-Klausel
auf die äußere Anfrage beziehen.
Diese Form der Anfrageschachtelung wird auch als korrelierte Unteranfrage bezeichnet.
SELECT e.FNAME, e.LNAME
FROM EMPLOYEE AS e
WHERE e.SSN IN (SELECT ESSN FROM DEPENDENT WHERE e.SEX = SEX);
Diese Anfrage ermittelt alle Verwandten (DEPENDENT), die das selbe Geschlecht haben wie
der in der Tabelle EMPLOYEE erfasste Mitarbeiter.
Jede korrelierte Unterabfrage kann durch Umschreibung in eine nicht-korrelierte Fassung
überführt werden. So lautet die Formulierung der obigen Abfrgae ohne geschachtelte
Unterabfrage:
SELECT e.FNAME, e.LNAME
FROM EMPLOYEE AS e, DEPENDENT AS d
WHERE e.SSN = d.ESSN AND e.SEX = d.SEX;
Die Formulierung als geschachtelte Unterabfrage ist damit nicht zwingend notwendig, kann
jedoch aus Gründen der Übersichtlichkeit gewünscht sein.
Die nähere Betrachtung der Anfragen aus den beiden Beispielen zeigt, dass die aus der
Tabelle DEPENDENT angefragten Daten lediglich zur Formulierung der Bedingung, nicht
jedoch zur Ausgabe herangezogen werden. Daher lässt sich die Bedingung unter
Verwendung des EXISTS-Operators umschreiben zu:
SELECT e.FNAME, e.LNAME
FROM EMPLOYEE AS e
WHERE EXISTS (
SELECT * FROM DEPENDENT
WHERE e.SSN = ESSN AND e.SEX = SEX);
EXISTS liefert den Boole'schen Wahrheitswert immer dann, wenn die (Unter-)Abfrage eine
nichtleere Menge ist, d.h. Daten enthält.
Anfragen die EXISTS oder IN beinhalten können auch durch linke Äußere Verbünde
ausgedrückt werden:
SELECT e.FNAME, e.LNAME
FROM EMPLOYEE AS e LEFT JOIN DEPENDENT AS d
ON e.SSN = d.ESSN AND e.SEX = d.SEX
WHERE d.SEX IS NOT NULL;
Eine ähnliche Funktion wie die EXISTS-Operation stellt ANY bereit, jedoch liefert diese die
durch die Unterabfrage angefragten Tupel zurück um sie an eine Bedingung zu knüpfen.
Nachfolgendes Beispiel zeigt die Ermittlung der Namen derjenigen Mitarbeiter, die mehr als
irgendein beliebiger Manager verdienen.
SELECT FNAME
FROM EMPLOYEE
WHERE SALARY > ANY (SELECT SALARY
FROM EMPLOYEE
WHERE SSN IN (SELECT SUPERSSN FROM EMPLOYEE));
Seite 18 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Aggregatfunktionen und Gruppierung
Über die Sortierung hinausgehend ist oftmals ein bestimmte Anordnung der durch eine
Anfrage ermittelten Ergebnistupel gewünscht, etwa als inhaltliche Gruppierung.
Gleichzeitig sind oft quantitative Aussagen über Eigenschaften der Resultatmenge - wie
größter oder kleinster Wert sowie Summen- oder Durchschnittsbildung - gewünscht.
Das Beispiel zeigt die Ermittlung der Summe aller Gehälter (SQL-Funktion SUM) sowie des
Maximal- (MAX), Minimal- (MIN) und Durchschnittsgehalts (AVG) für die Mitarbeiter der
Research-Abteilung.
Die genannten SQL-Funktionen werden als Aggregierungsfunktionen bezeichnet, da sie die
durch die Abfrage ermittelten Einzelwerte (d.h. die Einträge der Spalte SALARY) jeweils zu
genau einer Aussage verdichten.
SELECT SUM(SALARY), MAX(SALARY), MIN(SALARY), AVG(SALARY)
FROM EMPLOYEE, DEPARTMENT
WHERE DNO = DNUMBER AND DNAME="Research";
Mit der Funktion COUNT steht eine Möglichkeit zur Ermittlung der Mächtigkeit einer
Tupelmenge zur Verfügung.
SELECT COUNT(*)
FROM EMPLOYEE, DEPARTMENT
WHERE DNO = DNUMBER AND DNAME = "Research";
zeigt ihre Verwendung zur Ermittlung der Anzahl der Mitarbeiter der mit Research
bezeichneten Abteilung.
Als Argument der COUNT-Funktion kann mit DISTINCT ein Schlüsselwort angegeben
werden, welches die ausschließliche Zählung verschiedener Werte erwirkt.
SELECT COUNT(DISTINCT SALARY)
FROM EMPLOYEE;
Diese Anfrage ermittelt durch Nutzung dieses Schlüsselwortes die Anzahl der verschiedenen
Werte in der Spalte SALARY.
Häufig wird, wie in folgendem Beispiel gezeigt, eine Anfrage zur Ermittlung der Anzahl als
Unterabfrage formuliert und in der umgebenden Hauptabfrage mit einer Bedingung versehen.
SELECT LNAME, FNAME
FROM EMPLOYEE
WHERE (SELECT COUNT(*) FROM DEPENDENT WHERE SSN=ESSN) >= 2;
Neben den bisher gezeigten aggregierten Aussagen über eine Gesamtmenge besteht oftmals
der Wunsch nach von Ermittlung Aussagen dieses Stils über bestimmte Werteklassen
innerhalb der betrachteten Gesamtmenge. Hierzu dienen Gruppierungen der
Ausgangsmenge, auf welche dann die verschiedenen Aggregierungsfunktionen separat
angewandt werden können.
SELECT d.DNAME AS "Abteilung", COUNT(*) AS "Anzahl Mitarbeiter",
AVG(SALARY) AS "Durchschnittsgehalt"
FROM EMPLOYEE AS e, DEPARTMENT AS d
WHERE e.DNO = d.DNUMBER
GROUP BY DNO;
Ermittelt die Mitarbeiteranzahl pro Abteilung sowie der Berechnung des abteilungsinternen
Seite 19 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Durchschnittsgehalts.
Zur Realisierung wird die GROUP BY-Klausel verwendet, welche die Angabe eines oder
mehrerer Attribute zulässt anhand der die selektierte Menge partitioniert werden soll.
Die folgende Abfrage zeigt die Nutzung einer Verbundbedingung innerhalb einer
Gruppierungsanfrage, die Projektnummer und -name sowie vermöge der COUNT-Funktion die
Anzahl der das Projekt bearbeitenden Mitarbeiter ermittelt.
SELECT PNUMBER, PNAME, COUNT(*) AS "Anzahl Mitarbeiter"
FROM PROJECT, WORKS_ON
WHERE PNUMBER = PNO
GROUP BY PNUMBER, PNAME;
Durch zusätzliche Angabe der HAVING-Klausel kann die Menge der Gruppierungsresultate
mittels einer Bedingung beschränkt werden.
SELECT PNUMBER, PNAME, COUNT(*)
FROM PROJECT, WORKS_ON
WHERE PNUMBER = PNO
GROUP BY PNUMBER, PNAME
HAVING COUNT(*) > 2;
Die Abfrage ermittelt dieselben Resultat wie die Abfrage zuvor, jedoch nur für Projekte deren
Mitarbeiteranzahl größer 2 ist.
Die formulierte Beschränkung wirkt sich nicht auf die zur Berechnung herangezogene
Grundgesamtheit, sondern lediglich auf die Ausgabe der Gruppierungsergebnisse aus, die vor
der Auswertung der in der HAVING-Klausel formulierten Bedingung berechnet werden
müssen.
Zur Beschränkung der zur Berechnung heranzuziehenden Grundgesamtheit steht auch unter
Nutzung der GROUP BY-Klausel der durch WHERE formulierte Bedingungsteil der SELECTAnfrage zur Verfügung.
SELECT PNUMBER, PNAME, COUNT(*)
FROM PROJECT, WORKS_ON, EMPLOYEE
WHERE PNUMBER = PNO AND SSN = ESSN AND DNO=5
GROUP BY PNUMBER, PNAME;
Gruppierungsschritte können auch in Unterabfragen auftreten, wie im folgenden Beispiel zur
Ermittlung des Abteilungsnamens und der Anteil der darin arbeitenden Personen mit einem
Gehalt über 40000 für alle Abteilungen mit mindestens 2 Mitgliedern zeigt:
SELECT DNAME, COUNT(*)
FROM DEPARTMENT, EMPLOYEE
WHERE DNUMBER = DNO AND SALARY > 4000 AND DNO IN (
SELECT DNO
FROM EMPLOYEE
GROUP BY DNO
HAVING COUNT(*) > 2)
GROUP BY DNUMBER;
Seite 20 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Befehle zur Datenmanipulation
Die Befehle INSERT, UPDATE und DELETE und deren Anwendung werden als bekannt
vorausgesetzt.
INSERT
INSERT INTO tbl_name (col_name,...)? VALUES(constant|NULL ...)
UPDATE
UPDATE tbl_name SET col_name=expression, ... [WHERE search_condition]
DELETE
DELTE FROM tbl_name [WHERE search_condition]
Seite 21 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
3 Probleme bei Datenbankprogrammierung
3.1 Die Coddschen Regeln
Die Entwicklung von SQL zielt darauf ab, den Konzepten des relationalen Datenbankmodells
zu entsprechen. Dr. Codd veröffentlichte 12 Regeln (inklusive Regel 0 eigentlich 13), die eine
relationale Datenbank im strengen Sinne definieren.
Diese hierin erhobenen Forderungen sind jedoch so streng, dass sie bis heute kein System
vollständig erfüllt.
Die Regeln sind nachfolgend mit ihren englischsprachigen Originalbezeichnungen
wiedergegeben, da sich für sie bisher keine eindeutige und allgemeinverständliche deutsche
Übersetzung etablieren konnte.
Regel 1: The Information Rule (Darstellung von Informationen):
Alle Informationen in einer relationalen Datenbank (einschließlich Namen von Tabellen und
Spalten) sind explizit als Werte in Tabellen darzustellen.
Regel 2: Guaranteed Access Rule (Zugriff auf Daten):
Jeder gespeicherte Wert muss über Tabellenname, Spaltenname und Wert des
Primärschlüssels zugreifbar sein, wenn der zugreifende Anwender über hinreichende
Zugriffsrechte verfügt.
Regel 3: Systematic Treatment of Null Values (Systematische Behandlung von
Nullwerten):
Nullwerte müssen datentypunabhängig zur Darstellung fehlender Werte unterstützt werden.
Systematisch drückt hierbei aus, dass Nullwerte unabhängig von denjenigem Datentyp für den
sie auftreten gleich behandelt werden.
Regel 4: Dynamic On-line Catalog Based on the Relational Model (Forderung nach
einem Online-Datenkatalog (data dictionary) in Form von Tabellen):
Die Datenbank und ihre Inhalte werden in einem sogenannten Systemkatalog auf derselben
logischen Ebene wie die Daten selbst - also in Tabellen - beschrieben. Demzufolge läßt sich
der Katalog mit Hilfe der Datenbanksprache abfragen.
Regel 5: Comprehensive Data Sublanguage Rule (Abfragesprache):
Für das DBMS muss mindestens eine Sprache existieren durch die sich die verschiedenen
Inhaltstypen (Tabelleninhalte, Sichten, Integritätsstrukturen (Schlüsselbeziehungen,
Wertebereichseinschränkungen, Aufzählungstypen) sowie Zugriffsrechte) definieren lassen.
Regel 6: View Updating Rule (Aktualisierung von Sichten):
Sofern theoretisch möglich, müssen Inhalte von Basistabellen auch über deren Sichten
änderbar sein.
Regel 7: High-level Insert, Update, and Delete (Abfragen und Bearbeiten ganzer
Tabellen):
Innerhalb einer Operation können beliebig viele Tupel bearbeitet werden, d.h. die Operationen
werden grundsätzlich mengenorientiert ausgeführt. Hierfür ist eine so abstrahierte Sicht dieser
Operationen notwendig, dass keinerlei Information über die systeminterne Darstellung der
Tupel notwendig ist.
Regel 8: Physical Data Independence (Physikalische Datenunabhängigkeit):
Änderungen an der internen Ebene dürfen keine Auswirkungen auf die auf den
abgespeicherten Daten operierenden Anwendungsprogramme besitzen.
Seite 22 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Werden Daten demnach reorganisiert oder beispielsweise durch Indexe zugriffsbeschleunigt,
so darf eine solche Änderung die auf die Datenbank zugreifenden Anwendungsprogramme
nicht beeinträchtigen.
Regel 9: Logical Data Independence (Logische Datenunabhängigkeit):
Änderungen des konzeptuellen Schemas dürfen keine Auswirkung auf die
Anwedungsprogramme besitzen, solange diese nicht direkt von der Änderung betroffen sind.
Regel 10: Integrity Independence (Unabhängigkeit der Integrität):
In Verfeinerung der fünften Regel wird gefordert, dass alle Integritätsbedingungen
ausschließlich durch die Sprache des DBMS definieren lassen können müssen. Definierte
Integritätsbedingungen müssen in Tabellen abgespeichert werden und durch das DBMS zur
Laufzeit abgeprüft werden.
Im Mindesten werden folgende Forderungen durch verfügbare Systeme unterstützt:
Kein Attribut welches Teil eines Primärschlüssels ist darf NULL sein.
Ein Fremdschlüsselattribut muss als Wert des zugehörigen Primärschlüssels existieren.
Regel 11: Distribution Independence (Verteilungsunabhängigkeit):
Die Anfragesprache muss so ausgelegt sein, dass Zugriffe auf lokal gehaltene Daten identisch
denen auf verteilt gespeicherte Daten formuliert werden können.
Hieraus lässt sich auch die Ausdehnung der Forderungen nach logischer und physischer
Datenunabhängigkeit für verteilte Datenbanken ableiten.
Regel 12: Nonsubversion Rule (Kein Unterlaufen der Abfragesprache):
Definiert ein DBMS neben der High-level Zugriffssprache auch eine Schnittstelle mit
niedrigerem Abstraktionsniveau, dann darf durch diese keinesfalls eine Umgehung der
definierten Integritätsregeln möglich sein.
Zusätzlich fasst Codd in Regel 0 nochmals die Anforderungen dahingehend zusammen, dass
er postuliert, alle Operationen für Zugriff, Verwaltung und Wartung der Daten ausschließlich
mittels relationaler Fähigkeiten abzuwickeln.
Derzeit existiert kein am Markt verfügbares kommerzielles System welches alle zwölf Regeln
vollständig umsetzt.
Darüber hinaus greifen die Coddschen Regeln nicht alle Gesichtspunkte des praktischen
Datenbankeinsatzes auf. So bleiben Fragestellungen des Betriebs (wie Sicherungs-,
Wiederherstellungs- und Sicherheitsaspekte) eines DBMS völlig ausgeklammert.
Seite 23 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
3.2 Der „Impedance Mismatch“
3.2.1 Was ist der Impedance Mismatch?
Objektorientierte Programmiersprachen wie C++, C#, Visual Basic, Delphi oder Java stellen
Sprachelemente bereit, die nicht nur geeignet sind Benutzungsoberflächen zu entwickeln,
sondern ebenso, logische Probleme anzugehen.
Allerdings müssen auch OO-Entwickler zur dauerhaften Speicherung ihrer Daten auf einen
Hintergrundspeicher – in den meisten Fällen eine relationale Datenbank – zugreifen.
Die grundlegende Problematik des Impedance Mismatch entsteht durch die fehlende einfache
Abbildung von objektorientierten Strukturen auf das Datenmodell relationaler
Datenbankmanagementsysteme (RDBMS) und umgekehrt.
•
Relationale Datenbanken arbeiten mittels SQL immer auf Mengen von Tupeln,
objektorientierte Programmiersprachen hingegen verarbeiten Objekt für Objekt.
•
Die “Struktur” wird durch die Datenbank vorgegeben, “Verhalten” und „Logik“
weitgehend von Anwendungsprogrammen (Programmiersprache) vorgegeben.
•
Datenbank und Programmiersprache verwenden unterschiedliche Datentypen
(z.B. Möglicherweise: Integer auf DB-Seite ≠ Integer in Programmiersprache)
•
unterschiedliche Behandlung transienter und persistenter Objekte
Nicht jedes Objekt, das durch die Programmiersprache im Arbeitsspeicher erzeugt
wird, wird auch zwangsläufig persistent in der Datenbank gespeichert. Es muss eine
separate Logik zur Speicherung der Objekte geschaffen werden.
Die Überwindung des Paradigmenbruchs ist komplex und kaum ohne Reibungsverluste
möglich. Diese äußern sich nicht nur in einer längeren Zeit bis zur Marktreife der Applikation
sowie höheren Kosten für Design, Entwicklung und Qualitätssicherung, sondern
beeinträchtigen auch die Wartung und Erweiterbarkeit des Codes sowie die Performance der
Anwendungen.
Seite 24 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
3.2.2 Objekte versus Tabellen
Das relationale Modell beschreibt alle Daten anhand eines standardisierten Formats von
flachen Tabellen mit Zeilen und Spalten. Komplexe Datenstrukturen benötigen zumeist eine
Vielzahl von Tabellen, deren
Beziehungen untereinander (1:1,
1:N und M:N) auf Fremdschlüsseln
basieren. Vorgänge, die die
Geschäftslogik betreffen, werden
anhand von Quellen außerhalb der
Tabellen – beispielsweise über
eingebetteten SQL-Code, statische,
vordefinierte Stored Procedures
oder Trigger – zugewiesen.
Um effiziente Anwendungen zu
entwickeln, ist eine tiefgehende
Kenntnis der Tabellen, ihrer
Beziehungen untereinander und der
darin verankerten Stored
Procedures erforderlich.
Im Gegensatz dazu sind die
Klassen im objektorientierten
Modell in sich abgeschlossene
Einheiten. Wie relationale Tabellen
enthalten auch sie Informationen
(Attribute). Allerdings können hier
zusammenhängende Daten (etwa
eingebettete Klassen und
Collections) in der ContainerKlasse gespeichert werden, die
kein Fremdschlüsselkonstrukt wie
im relationalen Modell benötigt. Ein
weiterer bedeutender Unterschied
ist, dass die Geschäftslogik beim
objektorientierten Modell nicht von den Daten losgelöst ist. Stattdessen implementiert jede
Klasse ihre eigenen Methoden, die den Code für sämtliche Operationen auf den Attributen
ihrer Klasse enthalten. Die Methoden ihrerseits stellen Schnittstellen bereit, über die sie von
außen aufgerufen werden können.
Eine Überführung der Daten vom einen in das andere Modell muss vom
Applikationsentwickler dem Anwendungsfall entsprechend programmiert werden.
Seite 25 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
3.2.3 Die unterschiedlichen Paradigmen in der Praxis
Nachfolgend werden die Unterschiede zwischen objektorientiertem und relationalem
Modell anhand einer Anwendung zur Registrierung von Kraftfahrzeugen im Entwurf und der
Kodierung einer Datenbank aufgezeigt.
Ein Auto verfügt über bestimmte Merkmale wie Hersteller, Modell, Ausstattung, Baujahr,
Fahrzeugidentifikationsnummer (VIN) etc. Es hat einen oder mehrere Besitzer, einen oder
mehrere Fahrer, eine Reparaturhistorie und muss registriert werden.
Um die Daten eines KFZ im Objektmodell darzustellen, legen wir die Klassen Car, Person,
Owner, Driver und RepairHistory an. Die Klassen Owner und Driver erben die Attribute und
Methoden der Klasse Person. Diese lassen sich bei Bedarf mit eindeutigen Attributen und
Methoden erweitern.
Owner, Driver und RepairHistory werden als Collections in der Klasse Car betrachtet – etwa
als Collection von Verweisen bei den ersten beiden und als eingebettete Instanzen für
letztere.
Für eine praxisbezogenere Gestaltung geben wir an, dass ein Owner viele Cars besitzen und
ein Car mehrere Owner aufweisen kann. Um diese M:N-Beziehung zu implementieren, kann
die Klasse Owner eine Collection von Verweisen auf Car enthalten. (Wie oben angeführt, hat
Car bereits eine Collection von Owner.) Wir können auch eine M:N-Beziehung für Car
und Driver implementieren.
Schließlich geben wir eine Methode für die Klasse Car namens RegisterNew() an, die mit dem
Parameter VIN aufgerufen wird. Die gesamte Arbeit, die für die Registrierung eines neuen
KFZ erforderlich ist, kann hinter dieser Schnittstelle erledigt werden.
Um das gleiche Beispiel im relationalen Modell darzustellen, müssen, wie gezeigt, mehrere
Tabellen erstellt werden. Man beachte, dass zusätzliche Tabellen (CarDrivers und
CarOwners) erforderlich sind, um die M:N-Beziehung zwischen Owner, Driver und Car
abzubilden.
Die Zuordnung von Klassen zu einer oder mehreren Tabellen erfolgt durch die Abbildung der
Klassen-Attribute auf Felder der Tabellen innerhalb der Datenbank. Eine 1:1 Abbildung ist
hierbei in den seltensten Fällen möglich.
Weitere Informationen finden Sie unter [7].
Seite 26 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
3.3 Cursor-Konzept
Cursor - „Current Set of Records“
Ein Cursor ermöglicht die tupelweise Verarbeitung der Ergebnismenge einer
Datenbankabfrage. Die Verarbeitung einer Anfrage mittels Cursor erfolgt in vier Phasen:
Ein Cursor kann also verstanden werden als:
• Iterator über eine Liste von Tupeln
• Hauptspeicherbereich zur Verarbeitung von SQL-Anweisungen
• SQL-Objekt, das mit einem bestimmten Tabellenausdruck (als Ergebnis einer Abfrage)
verbunden ist
Cursoroperationen im Überblick:
– Cursor deklarieren (Zuordnung des Select-Statements)
– Cursor öffnen (Tupel der Anfrage werden zur Verfügung gestellt)
– Operation fetch auf dem geöffneten Cursor verwenden (Zugriff auf ein Tupel des
Cursors und Datentransfer in das Anwendungsprogramm sowie Weitersetzen des
Cursor-Zeigers)
– Cursor schließen (Speicherplatzfreigabe)
– (positioniertes update und delete - in Bezug auf das gerade im Zugriff befindliche Tupel
- …where current of …)
Seite 27 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
3.4 Andere Lösungsansätze zur Überwindung des Impedance
Mismatch
Um die Lücke zwischen objektorientierten Anwendungen und einer relationalen Datenbank zu
schliessen, gibt es drei Lösungsansätze:
Objektorientierte Erweiterungen relationaler Datenbanken
Die Hersteller relationaler Datenbankmanagementsysteme haben große Anstrengungen
unternommen Ihre Datenbanken in Richtung Objektorientiertheit zu erweitern – die Lücke
wirklich geschlossen haben sie allerdings nicht.
Einsatz von Mapping-Tools
Zur Abbildung von Objekten auf Tabellen einer relationalen Datenbank werden zahlreiche
Mapping-Tools angeboten. Die Abbildungsvorschriften sind jedoch nach wie vor vom
Applikations-Entwickler anzugeben. Der einzige Unterschied ist der, dass nun für die
Abbildung nicht mehr programmiert sondern lediglich konfiguriert werden muss. Meist sind die
Abbildungsvorschriften in einer speziellen, toolspezifischen XML-Syntax zu verfassen.
Einige dieser Mapping-Tools sind beispielsweise:
Hibernate
- http://www.hibernate.org/
- für Java, .Net
- für Java, .Net, Ruby
iBatis
- http://ibatis.apache.org/
Einführung einer objektorientierten oder postrealtionalen Datenbank
Statt der Verwendung einer relationalen Datenbank kann auch eine objektorientierte
Datenbank eingesetzt werden. Diese Art der Datenbanken konnte sich jedoch bisher nicht
durchsetzen.
Das einzige postrelationale Datenbanksystem ist Cache von InterSystems. Es verspricht die
Vorteile beider Welten – der relationalen und der objektorientierten – zu vereinen. Einen
großen Verbreitungsgrad hat dieses Datenbankmanagementsystem jedoch trotz des
interessanten Ansatzes nicht gefunden.
Seite 28 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
4 Zugriff auf Datenbanken
4.1 Allgemein
Von einer DB-API werden typischerweise folgende Funktionen erwartet:
•
Verbindungsaufbau zu einer Datenbank
•
Datenbankverbindung schließen
•
SQL-Anweisung an DB schicken, sofort ausführen
•
SQL-Anweisung an DB schicken, analysieren und übersetzen
•
Datensätze holen, Cursor (zur zeilenweisen Verarbeitung der Ergebnismengen) => vgl.
Impedance Mismatch
•
Transaktionssteuerung
•
Statusabfrage
Seite 29 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
4.2 Verarbeitung einer SQL-Anfrage
Eine SQL-Anfrage wird prinzipiell in Folgenden Schritten von einer SQL-Datenbank
abgearbeitet:
1. Parsen des Statements: Überprüfung, ob es sich um ein syntaktisch korrektres SQLStatement handelt.
2. Validierung: Überprüfung des SQL-Statements gegenüber dem Datenbank-Katalog.
D.h. Prüfung, ob verwendete Tabellen existieren, ggf. Überprüfung von
Berechtigungen.
3. Optimierung: Analyse der Zugriffsstrategie und Auffinden eines optimalen Vorgehens.
Verwendung von Indices, Umstellung von Joins,etc. Aus dieser Optimierung entsteht
ein Zugriffsplan („binäre Form des SQL-Statements“)
4. Ausführung des Statements und Ermittlung der Ergebnismenge bzw. Modifizierung
der Daten.
Verarbeitung einer SQL-Abfrage
Seite 30 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
5 Datenbankprogrammierung innerhalb der Datenbank Stored Procedures & Trigger
Stored Procedures (SP’s) oder auch User Defined Functions (UDF’s) gennant
5.1 Was sind Stored Procedures?
•
In der Datenbank gespeicherte Programmteile
•
Datenbankobjekte
•
Vom DB-Benutzer definiert
•
Werden im DB-Server-Prozess ausgeführt
•
Erweitern die Funktionalität des DBMS
5.2 Wozu dienen Stored Procedures?
•
SP’s dienen der Erweiterung der Funktionalität von DBMSn
- wenn die Möglichkeiten von SQL nicht genügen
- und wenn eine Verarbeitung am Server sinnvoller ist
•
Trigger dienen dazu:
- um die Daten der DB konsistent zu halten
- um mehr semantische Informationen zu definieren
(z.B. Bei Abbuchung vom Girokonto: Prüfung ob der Kreditrahmen auf dem Konto
überschritten wird, wenn der gewünschte Betrag abgebucht wird – ACHTUNG: Viele
Trigger machen den DB-Zugriff langsam!)
Mehr zum Thema Trigger siehe [8] http://www.dbmsmag.com/9605d17.html
•
Methoden von Objekten
- bei objekt-relationalen DBn
5.3 Wer bietet Stored Procedures?
Folgende DBMS bieten Stored Procedures:
• IBM DB2
• Microsoft SQL-Server
• Oracle
• PostgreSQL
• Sybase
• MySQL (eingeschränkt seit Version 5)
Stored Procedures werden nicht unterstützt von:
• Dateisystem Datenbanken (z.B. Access)
Seite 31 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
5.4 Wann sollten Stored Procedures verwendet werden?
•
Sobald eine Stored Procedure (SP’s) ein wenig komplexer wird ist sie meist abhängig
vom verwendeten Datenbank-Management-System. SP’s in Oracle sind also
beispielsweise nicht kompatibel zu SP’s in DB2!
Verwenden Sie Stored Procedures also nur dann, wenn Sie sicher sind mit Ihrer
Applikation nur ein DBMS unterstützen zu müssen!
•
Stored Procedures bringen oft einen kleinen Performance-Vorteil, weil die SP nur
einmalig „kompiliert“ werden muss und danach in binärer Form in der DB liegt. Führt
man eine SP aus kann daher direkt bei „Schritt 4. - der Ausführung“ (vgl. Kapitel 4.2
Verarbeitung einer SQL-Anfrage) begonnen werden. Bei den meisten modernen
Rechnerarchitekturen spielt dies jedoch keine ausschlaggebende Rolle, da in den
meisten höheren Programmiersprachen auch sog. „Prepared Statements“ unterstützt
werden, die den selben Vorteil bieten – optimieren Sie daher lieber an anderer Stelle!
•
SP’s sind von Vorteil, wenn Sie Ihre Anwendung von der darunter liegenden
Datenstruktur (Tabellen) entkoppeln wollen – hierfür waren SP’s früher immer das
Mittel der Wal und gehörten zu sauberem Applikationsdesign oft dazu. Heute
verwendet man oft innerhalb der Applikation einen dünnen Mapping Layer, der die
Business-Objekte aus der Ebene der Geschäftslogik von der Datenbank entkoppelt
(Hierzu später mehr).
•
Die Verwendung von SP’s ist eine grundsätzliche Designentscheidung – und
eine Glaubensfrage!
5.5 Stored Procedures / Trigger (bei Oracle)
5.5.1 Allgemein
•
Programmiersprache bei Oracle: PL/SQL
o Procedural Language / Structured Query Language
o proprietäre Programmiersprache der Firma Oracle
o mittlerweile von den meisten anderen DBMS-Herstellern übernommen
o Verbindet den Komfort einer prozeduralen Sprache mit der Geschwindigkeit
von SQL
o Unterstützt Variablen, Bedingungen, Schleifen, Ausnahmen
o typenstrenge Programmiersprache
o Oracle PL/SQL Tutorial unter: [11] http://www.datenbank-plsql.de
[12] http://www.orafaq.com/faq/plsql/
[13] http://www.oracle10g.de/oracle_10g_documentation/appdev.101/b10807/toc.htm
•
•
•
•
•
Programmiersprache bei DB: SQL PL
Programmiersprache bei MS-SQL-Server und Sybase: Transact SQL
C, C++ und JAVA sind auch möglich
Cursor verbinden SQL mit prozeduraler Verarbeitung
Besondere Konstrukte für Datenzugriff
- for cursor loop
- Datensatztyp-Deklaration
- Collection-Typen
Seite 32 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
5.5.2 Aufbau von Stored Procedures
•
Deklarationsteil:
- Deklaration von Variablen, Cusor, Collections, …
•
Ausführungsteil:
- Programmierung des Algorithmus
- Transaktionssteuerung
•
Ausnahmebehandlung
- Reaktionen auf Fehler werden hier zusammengefasst
- Damit wird GOTO vermieden
5.5.3 Beispiel (PL/SQL)
create or replace function Leihe
(resID in number))
return varchar2 as
varKundeID
varModellID
varBeginn
varEnde
varStartKm
NUMBER;;
NUMBER;;
DATE;
DATE;
DATE;
DATE;
NUMBER;;
varKennzeichen CHAR(
);
CHAR(10);
CURSOR curReservierung IS
SELECT KundeID,, ModellID,, Beginn,, Ende
FROM Reservierung
WHERE ID = resID;;
cursor curCar is
select KMSTAND,, KENNZEICHEN from auto,, reservierung
where auto.modell=
=reservierung.modellid
and reservierung.id = resID
and auto.kennzeichen not in
(
select kennzeichen from leihvertrag,, auto,, reservierung
where auto.modell=
=reservierung.modellid
and reservierung.id = resID
and auto.kennzeichen=
=leihvertrag.autoid
and leihvertrag.ende>
>sysdate
);
BEGIN
/* Reservierung mit der ReservierungsID holen */
OPEN curReservierung;;
FETCH curReservierung INTO varKundeID,, varModellID,, varBeginn,, varEnde;;
Seite 33 von 104
Dipl.-Ing. (FH) D. Schiffer
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
/* Freies Auto holen */
OPEN curCar;;
FETCH curCar INTO varStartKm,, varKennzeichen;;
/* Prüfen ob die abgerufene Reservierung mit dem heutigen Datum übereinstimmt */
if to_char((varBeginn,,'dd.mm.yyyy')=
)=to_char(
(sysdate,,'dd.mm.yyyy')) then
)=
if curCar%
%rowcount <= 0 then
return ('Kein freies Auto !!!');
);
end if;
if;
/* Auto ist verfügbar - reservierung in leihe umwandeln */
/* Eintrag in Leihvertrag einfügen */
Insert Into leihvertrag (id,, kundeid,, autoid,, beginn,, ende,, startkm))
values(
values(leihseq.nextval,, varKundeID,, varKennzeichen,, varBeginn,, varEnde,,
varStartKm);
);
/* Reservierungseintrag löschen */
Delete From reservierung where id = resID;;
return varKennzeichen;;
else
);
return ('Reservierung ist nicht von Heute !!!!');
end if;
if;
END;
END;
5.5.4 Ausnahmebehandlung
Benutzerdefinierte Ausnahmen:
declare ausnahme1 exception; …
begin … raise ausnahme1 …
exception
when ausnahme1 then <aktion>
end;
Daten-Ausnahmen (vordefiniert), z.B.
when data_not_found
when max_open_cursors
Seite 34 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
5.5.5 Aufbau von Triggern (PL/SQL)
CREATE [OR REPLACE]] TRIGGER <trigger_name>
>
{BEFORE|
BEFORE|AFTER}
AFTER} {INSERT|
INSERT|DELETE|
DELETE|UPDATE}
UPDATE}
ON <table_name>
>
[REFERENCING [NEW AS <new_row_name>]
>] [OLD AS <old_row_name>]]
>]]
[FOR EACH ROW [WHEN (<trigger_condition>)]]
>)]]
(<
<trigger_body>
>
5.5.6 Beispiel (PL/SQL)
CREATE TRIGGER mitarbeiter_trig_gehalt_biu
BEFORE INSERT OR UPDATE OF gehalt ON mitarbeiter
FOR EACH ROW
-- Triggername mitarbeiter_trig_gehalt_biu (_biu steht für B-efore I-nsert U-pdate )
-- Tabelle: MITARBEITER
-- Trigger soll nur ziehen, wenn Gehalt kleiner 5000 ist oder Neues Gehalt kleiner altem Gehalt
-- in der folgenden WHEN erfolgt der Zugriff auf :old und :new OHNE vorangestellte :
WHEN (new.gehalt <= 5000 or new.gehalt < old.gehalt))
DECLARE
v_mitarbeiter_name VARCHAR2((255):=
):=null
):=null;
null;
-- Cursor holt "Name, Vorname" von Mitarbeitertabelle
-- Explizite Definition des Cursors hat
-- Performance-Vorteile gegen "select into <variable>" im Trigger-Body
cursor c_name is :select name||
||',
'||
||vorname
||
||
from mitarbeiter
where mitarbeiternummer = :NEW.MITARBEITERNR;;
BEGIN
-- Mitarbeiter-Name ermitteln und in Variable v_mitarbeiter_name speichern
open c_name;;
fetch c_name into v_mitarbeiter_name;;
close c_name;;
-- Bei Gehalt kleiner 5000 Exception auslösen
--> Fehler wird in der Regel bis zur Anwendung hochgereicht
-- Exception-Nr -20000 - -29999 können selbst definiert werden
IF :NEW.GEHALT <= 5000 THEN
RAISE_APPLICATION_ERROR((
-20901,,
'Manager Gehalt für Mitarbeiter '||
||v_mitarbeiter_name||
||'
||
|| zu klein!'
);
END IF;
IF;
IF :NEW.GEHALT < :OLD_GEHALT THEN
THEN
RAISE_APPLICATION_ERROR((
-20902,,
'Gehaltskürzung für Mitarbeiter '||
||v_mitarbeiter_name||
||'
||
|| nicht erlaubt!'
);
END IF;
IF;
END;
END;
/
Seite 35 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
5.6 Erstellen von Stored Procedures
Für die Erstellung von SP’s und Triggern greift man unter Oracle am besten auf das
kommerzielle Toad der Firma Quest (http://www.toadsoft.com/ ) oder den Oracle SQLDeveloper (http://www.oracle.com/technology/software/products/sql/index.html ) zurück.
Letzterer muss leider separat von der Oracle Seite heruntergeladen werden, hierfür ist
allerdings eine Registrierung notwendig. Ansonsten ist der Einsatz kostenlos.
Handbuch des Oracle SQL-Developers:
http://download-uk.oracle.com/docs/cd/B32485_01/doc/appdev.110/b31695/toc.htm
Seite 36 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
5.7 Aufruf und Ablauf von Stored Procedures
5.7.1 Aufruf einer Stored Procedure aus dem Client-Programm
(z.B. Java, JDBC)
vgl. hierzu: [9] http://www.onjava.com/pub/a/onjava/2003/08/13/stored_procedures.html
…
public static void setDeathAge((Poet dyingBard,, int age))
throws SQLException
{
Connection con = null;
null;
CallableStatement proc = null;
null;
try
{
con = connectionPool..getConnection();
();
proc = con..prepareCall(("{ call set_death_age(?, ?) }");
proc..setString((1,, dyingBard..getName(());
proc..setInt((2,, age);
);
proc..execute();
();
}
finally
{
try
{
proc..close();
();
}
catch (SQLException e)) {}
con..close();
();
}
} ….
5.7.2 Stored Procedure auf dem DB-Server
create procedure set_death_age((poet VARCHAR2,, poet_age NUMBER))
poet_id NUMBER;;
begin
SELECT id INTO poet_id FROM poets WHERE name = poet;;
INSERT INTO deaths (mort_id,, age)) VALUES (poet_id,, poet_age);
);
end set_death_age;
Seite 37 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6 Java Database Connectivity (JDBC)
6.1 Allgemein
•
Ist ein Call Level Interface (CLI) (wie ODBC), d.h. Sammlung von Methode / Klassen
(vgl. S. 4)
•
Definiert eine SQL „Schnittstelle“ für Java (kein Interface im Sinne von Java)
•
Ist Basis für höhere Programmierschnittstellen (Hibernate, SQLJ - Embedded SQL für
Java)
•
Ein JDBC-Treiber ist eine spezielle Implementierung des JDBC-Interface
•
Austauschbarkeit des Datenbanksystems wird vereinfacht
•
JDBC-spezifische Klassen und Interfaces sind im Paket java.sql definiert
•
Unterschiede zu ODBC: Methoden statt Funktionen, Objekte statt Handles (kurz:
objektorientiert, statt prozedural)
6.2 Grundstruktur eines JDBC-Programms
•
Einbinden der JDBC-Klassen
•
JDBC Treiber laden
•
Verbindungsaufbau zur Datenbank
•
Eigenschaften der Verbindung spezifizieren
•
Zugriff auf Daten mittels SQL
•
Ergebnisverarbeitung mittels Cursor
•
Ausnahme- und Fehlerbehandlung
•
Verbindungsabbau (Wichtig! – niemals vergessen)
Seite 38 von 104
R
e
i
h
e
n
f
o
l
g
e
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.3 Einbinden der JDBC-Klassen
Import java.sql.*;
Die wichtigsten Klassen / Interfaces aus dem Package java.sql.*
Treibermanager zur Verwaltung von Treibern
Repräsentiert einen bestimmten Treiber
Repräsentiert eine DB Verbindung
SQL Anweisung
Ergebnis nach Ausführung einer SQLAnweisung
ResultSetMetaData Metainformationen über Tabellen, Attribute
DatabaseMetaData
Metainformationen Datenbankverbindung
SQLException
Bei fehlerhaften SQL-Anweisungen
DriverManager
Driver
Connection
Statement
ResultSet
Seite 39 von 104
Class
Interface
Interface
Interface
Interface
Interface
Interface
Exception
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.4 Architektur
JDBC implementiert das Bridge-Pattern nach GOF [14]:
Die eigentliche Applikation als Client im oberen Bild dargestellt verwendet lediglich die
abstrakte Schnittstelle, die darunterliegende Implementation ist der Anwendung selbst egal
•
JDBC spezifiziert nur Schnittstellen
•
Implementation durch DBMS-Hersteller: Treiber
•
Treibermanager lädt Treiber
•
Treiber stellt Verbindung zur Datenbank her
Seite 40 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
•
Es gibt 4 Treibertypen
•
Typ 3 und 4 sind vorzuziehen:
pure JAVA
Seite 41 von 104
Dipl.-Ing. (FH) D. Schiffer
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
6.5 Verbindungsaufbau zur Datenbank
JDBC unterscheidet 4 verschiedene Treibertypen:
Typ 1: JDBC-ODBC Bridge
•
•
•
Verwendung eines externen ODBCTreibers
Muß mit der Java-Anwendung installiert
werden
Übersetzt Java Methodenaufrufe in
Prozeduraufrufe der ODBC C-Bibliothek
Typ 2: Native Java-Library Treiber
•
•
JDBC-Treiber, der anstelle eines ODBCTreibers auf einen datenbankspezifischen
Treiber zugreift
z.B. Oracle8 OCI-Treiber, der Net8
verwendet
Typ 3: JDBC-Net Treiber
•
•
•
•
keine direkter Datenbankzugriff
Zugriff über „Middleware“ = Datenbankgateway
Treiber ist vom
Datenbankgateway abhängig
Nützlich z.B. zur
Umgehung einer
Firewall
Typ 4: Native-Protocol Treiber
•
•
•
Vollständig in Java implementierter
Treiber
Wird vom DBMS-Hersteller zur
Verfügung gestellt (z.B. Oracle8 Thin
Driver)
Direkte Kommunikation mit dem DBMS
Seite 42 von 104
Dipl.-Ing. (FH) D. Schiffer
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Die Klasse java.sql.DriverManager
Driver registerDriver (Driver
driver)
Connection getConnection
(String url, String user,
String password)
Registriert einen (neuen) nicht-ODBC-Treiber
beim Treibermanager. Dieser Treiber wird zum
Verbindungsaufbau verwendet
Baut über den registrierten Treiber eine
Verbindung zur Datenbank auf.
Datenbank
Klassenname für den JDBC-Treiber
DB2/Derby
com.ibm.db2.jcc.DB2Driver
Oracle
oracle.jdbc.driver.OracleDriver
mSQL
COM.imaginary.sql.msql.MsqlDriver
MySQL
com.mysql.jdbc.Driver
org.gjt.mm.mysql.driver
früher:
Borland JDataStore
com.borland.datastore.jdbc.DataStoreDriver
Borland Interbase
interbase.interclient.Driver
Adabas D
de.sag.jdbc.adabasd.ADriver
SYBASE ASE
com.sybase.jdbc.SybDriver
IDS Server
ids.sql.IDSDriver
Datenbank
Subprotokoll
Beispiel-URL
Derby
derby:net
jdbc:derby:net://localhost:1527/
ODBC-Datenquellen
odbc
jdbc:odbc:Pflanzen
mSQL
msql
jdbc:msql://host:1234/database
Interbase
interbase
jdbc:interbase://localhost/dabase.gdb
Oracle Thin
oracle:thin
jdbc:oracle:thin:@host:1243:database
Oracle Fat
oracle:oci8
jdbc:oracle:oci8:@database
IBM DB2
Db2
jdbc:db2://database
Sybase
sybase:Tds
jdbc:sybase:Tds:host:1234/database
MySQL
mysql
jdbc:mysql://host/database
An Hand der unterschiedlich aufgebauten URL erkennt der Driver-Manager, welcher der
datenbankspezifischen Treiber geladen und verwendet werden soll.
Das Interface java.sql.DatabaseMetaData
String getURL()
String.getDriverName()
String.getDriverVersion()
Boolean.supportsGroupBy ()
Liefert die Datenbank-URL
Liefert den Treibernamen
Liefert die Treiberversion
Überprüft, ob die Datenbank die SQL Group ByKlausel unterstützt
Seite 43 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.5.1 Laden eines JDBC Treibers
6.5.2 Verbindung zur Datenbank: Connection Objekt erzeugen
Beispiel:
// Aufbau der Verbindung
DriverManager.registerDriver (
new oracle.jdbc.driver.OracleDriver ());
// oder: Class.forName("oracle.jdbc.driver.OracleDriver"); // Vorteil dieser
// Variante, man kann
//den String z.B. aus
//einer Konfiguration
// einlesen
String url = "jdbc:oracle:thin:@<host>:<port>:<database>";
// oder: String url = "jdbc:oracle:oci8:@<database>";
Connection con = DriverManager.getConnection (url,“Scott”, “Tiger”);
// Abfrage von Metadaten
DatabaseMetaData dma = con.getMetaData();
System.out.println(“Connected to “+ dma.getURL());
System.out.println(“Driver: “+ dma.getDriverName());
System.out.println(“Version: “+ dma.getDriverVersion());
Seite 44 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Aufgabe:
Vervollständigen Sie folgende Klasse unter der Annahme, dass der Datenbankhost jeweils der
aktuelle Rechner und der Datenbankname „testdatabase“ ist.
Was für ein Design-Pattern implementiert die Klasse ConnectionHelper?
import java.sql.*;
public class ConnectionHelper {
public final static String DB2 = "db2";
public final static String ORACLE = "oracle";
public final static String MYSQL = "mysql";
private ConnectionHelper() {
super();
}
public static synchronized Connection getConnection(String dbtype)
throws ClassNotFoundException, SQLException {
Connection con = null;
String url = null;
String user = null;
String pwd = null;
if (dbtype.equals(DB2)) {
Class.forName(__________________________________);
url = "jdbc:db2://_______";
user = "fridolin";
pwd = "sicher";
} else if (dbtype.equals(ORACLE)) {
Class.forName(__________________________________
url = "jdbc:oracle:thin:@_______:1243:_______";
user = "scott";
pwd = "tiger";
} else if (dbtype.equals(MYSQL)) {
Class.forName__________________________________);
url = "jdbc:mysql://_______/_______";
user = "ich";
pwd = "privat";
}
If (______________) // nur dann Verbindung aufbauen, wenn Parameter
// passt
con = DriverManager.getConnection(url, user, pwd);
}
return con;
}
}
Seite 45 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.6 Datenzugriff mittels SQL
•
•
•
•
•
•
•
•
•
•
Statements sind die Arbeitspferde in JDBC
Mit ihnen werden Daten manipuliert und gesichtet
Es gibt 3 Typen mit verschiedenen Aufgaben
Statement
o Zur Datenmanipulation und zum Erzeugen von ResultSets.
PreparedStatement
o Precompiled Statement
PreparedStatement pstmt = connnection.prepareSatement(
"INSERT INTO emp VALUES (?,?,?,?,?);");
CallableStatement
o zum Ausführen von Stored Procedures
Statement kann nicht mit new erzeugt werden, sondern das Connection-Objekt dient
als Factory für das Statement
Es wird bei einem Connection Objekt angefordert
Dadurch bleibt der Bezug zur Connection bestehen
Beispiel:
Statement stmt = connection.createStatement()
6.6.1 Dynamisches SQL mit Statments
Die komplette SQL-Anweisung wird bei jeder Ausführung durch den Precompiler des DBMS
erneut verarbeitet.
Das Interface java.sql.Statement
ResultSet executeQuery(String sql)
Integer executeUpdate(String sql)
Boolean execute(String sql)
Erlaubt ausschließlich die Ausführung von
SELECT-Statements
INSERT, UPDATE oder DELETE
Statements und SQL DDL Statements
erlaubt
Beliebiges SQL Statement erlaubt
Beispiel:
Statement stmt = con.createStatement();
// SELECT Statement: Menge von Tupeln ist Ergebnis
ResultSet rs = stmt.executeQuery(“SELECT name, salary FROM emp
WHERE name LIKE ‘Smith’ AND salary > 1000”);
// UPDATE Statement: Ergebnis (count) ist Anzahl
// der Änderungen oder 0 für Statements
// die kein Ergebnis zurückliefern
Integer count = stmt.executeUpdate(“UPDATE emp SET sal = sal * .95“);
Seite 46 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.6.2 Vorkompiliertes SQL
Die SQL-Anweisung wird unter Angabe von Parametern nur einmal kompiliert und
anschließend gespeichert.
Bei jeder Ausführung müssen nur noch die Parameter eingestellt werden. Dadurch führt die
Verwendung der Klasse PreparedStatement in der Regel zu einer besseren Performance,
denn die SQL-Anweisungen, die mittels execute(), executeQuery() oder
executeUpdate() an die Datenbank gesendet werden, haben bis zur Ausführung im
Datenbanksystem einige Umwandlungen vor sich:
Zuerst müssen sie auf syntaktische Korrektheit getestet werden. Dann werden sie in einen
internen Ausführungsplan der Datenbank übersetzt und mit anderen Transaktionen optimal
verzahnt (vgl. dynamisches SQL bei ODBC & Embedded SQL).
JDBC unterstützt eine Vorübersetzung, die sich Prepared Statements nennt. Vorbereitet (engl.
prepared) deshalb, weil die Anweisungen in einem ersten Schritt zur Datenbank geschickt und
dort in ein internes Format umgesetzt werden.
Später verweist ein Programm auf diese vorübergesetzten Anweisungen, und die Datenbank
kann sie schnell ausführen, da sie in einem optimalen Format vorliegen.
Der Geschwindigkeitsvorteil macht sich immer dann besonders bemerkbar, wenn Schleifen
Änderungen an Tabellenspalten vornehmen. Das kann durch die vorbereiteten Anweisungen
schneller geschehen.
Das Interface java.sql.PreparedStatement implementiert im Wesentlichen dieselben
Methoden wie das Interface java.sql.Statement.
Seite 47 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Genauso wie ein Connection-Objekt eine Methode für ein Statement-Objekt anbietet, werden
PreparedStatement-Objekte angelegt. Dazu dient die Methode prepareStatement().
Als Argument wird eine SQL-Zeichenkette übergeben, die den gleichen Aufbau wie etwa ein
executeUpdate() hat.
Unterschied: Bei den normalen Statement-Objekten können dynamische Einträge mit in den
SQl-Stament-String eingebaut werden, indem einfach der SQL-String entsprechend
konkateniert wird. Bei vorbereiteten Anweisungen ist dies nicht mehr möglich.
Damit jetzt auch eine vorbereitete Anweisung Parameter enthalten kann, werden in die
Zeichenkette Platzhalter mit einem Fragezeichen eingefügt.
Die Ersetzung dieser Platzhalter im Statement erfolgt der Reihenfolge nach.
Seite 48 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Beispiel:
// SELECT Statement
String sql = “SELECT name, salary FROM emp
WHERE name LIKE ? AND salary > ?“;
PreparedStatement prepStmt = con.prepareStatement(sql);
prepStmt.setString(1, “Smith“);
prepStmt.setInt(2, 1000);
ResultSet rs = prepStmt.executeQuery();
// UPDATE Statement
String sql = “UPDATE emp SET sal = sal * ?
WHERE ename = ?“;
PreparedStatement prepStmt = con.prepareStatement(sql);
Float ratio = 0.95;
String name = “Smith”;
prepStmt.setFloat(1, ratio); // setzen der Parameter
prepStmt.setString(2, name);
Integer count = prepStmt.executeUpdate();
Seite 49 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.7 Ergebnisverarbeitung mittels ResultSet
Auch in JDBC ist das Problem des „Impedance Mismatch“ nach wie vor präsent.
D.h. ein SQL-Befehl liefert eine Menge von Zeilen als Ergebnis. Im Java-Programm müssen
die Zeilen einzeln nacheinander verarbeitet werden.
6.7.1 ResultSets
Zur Überwindung des „Impedance Mismatch“ wird für einen SQL-Befehl ein Cursor definiert.
In Java stellt dieser Cursor ein Objekt der Klasse ResultSet dar.
Der Cursor ist ein Objekt, welches die Zeilen der Ergebnismenge des SQL-Befehls als Wert
annimmt.
Das ResultSet wird von einem Statement erzeugt.
Jedes ResultSet hat für sich einen Cursor, der auf die aktuelle Zeile der Ergebnismenge
zeigt
Innerhalb des ResultSet werden Typkonvertierungen zwischen SQL-Datentypen und JavaDatentypen vorgenommen:
SQL-Datentyp
Java-Datentyp
CHAR, VARCHAR, …
String, InputStream
BINARY, :::
Byte[], InputStream
BIT
Boolean
TINYINT, SMALLINT
Short, int
BIGINT
Long, int
REAL
Float
DOUBLE, FLOAT
Double
DECIMAL, NUMERIC
Java.math.BigDecimal
DATE
Java.sql.Date
Nicht verwechseln mit java.util.Date
TIME
Java.sql.Time
TIMESTAMP
Java.sql.Timestamp
Wichtige Methoden der Klasse ResultSet:
Boolean next()
Setzt den Satzzeiger um einen Satz weiter.
Initialzustand ist vor dem ersten Datensatz.
XXX getXXX(int columnIndex)
Liest den Wert der Spalte an der Position
columnIndex des aktuellen Datensatzes aus. XXX
steht für einen bestimmten Datentyp.
XXX getXXX(String columnName) Wie oben, jedoch durch Angabe des
Spaltennamens columnName anstelle des Index.
ResultSetMetaData
Liefert eine Beschreibung der aktuellen Spalte des
getMetaData()
ResultSet (Anzahl, Typen, Eigenschaften)
Void updateXXX(int
Überschreibt den Wert der Spalte an der Position
columnIndex, XXX x)
columnIndex des aktuellen Satzes mit dem Wert x.
XXX steht für einen bestimmten Datentyp.
Void updateRow()
Schreibt den aktuellen Datensatz in die Datenbank.
Seite 50 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.7.2 Ergebnisabfrage
Durch Angabe des Spaltenanmen:
String name;
int salary;
while(rs.next()) {
name = rs.getString(“name“);
salary = rs.getInt(“salary“);
System.out.println(“Name: “+name);
System.out.println(“Gehalt: “+salary);
}
Vorteil: Die Abfrage kann umgebaut werden (Änderung der Reihenfolge der AusgabeSpalten), der Datenzugriff funktioniert weiterhin.
Durch Angabe des Spaltenindex:
String name;
int salary;
while(rs.next()) {
name = rs.getString(1);
salary = rs.getInt(2);
System.out.println(“Name: “+name);
System.out.println(“Gehalt: “+salary);
}
6.7.3 Datensätze ändern
Änderung der Cursor-Werte und Update der Datenbank, über die Methode updateRow():
int curSal;
while(rs.next()) {
curSal = rs.getInt(“salary“);
if (curSal > 2000) {
curSal = curSal * 0.95;
rs.updateInt(“salary“, curSal);
rs.updateRow();
}
}
Vergessen Sie nicht, dass Sie auch direkt über ein Statement.executeUpdate Updates
und Inserts ausführen können.
Das gleichzeitige Lesen und Ändern der Daten ist eher unüblich.
Seite 51 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.7.4 Datensätze einfügen
Zum Einfügen eines neuen Datensatz in die Datenbank sind folgende Schritte auf dem
ResultSet auszuführen:
•
Verwendung des „Insert“-Buffer (insertRow)
•
Einstellen des Satzzeigers auf die insertRow mit Methode moveToInsertRow(),
mit moveToCurrentRow() wird wieder zurück zur Ergebnismenge gesprungen
•
Erzeugung des Datensatz mit den Methoden updateXXX(). Jede Spalte der
insertRow muß belegt werden.
•
Aufruf der Methode insertRow() erzeugt den entsprechenden Datensatz in der
Datenbank
Beispiel:
String newName = “Allen“;
int newSal = 1250;
rs.moveToInsertRow();
rs.updateString(“name“, newName);
rs.updateInt(“salary“, newSal);
rs.insertRow();
rs.moveToCurrentRow();
Vergessen Sie nicht, dass Sie auch direct über ein Statement.executeUpdate Updates
und Inserts ausführen können.
Das gleichzeitige Lesen und Ändern der Daten ist eher unüblich.
Seite 52 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
6.7.5
Dipl.-Ing. (FH) D. Schiffer
Batch Updates
Das Einfügen und Ändern großer Mengen von Daten kostet viel Zeit, da für jede Modifikation
ein INSERT oder UPDATE über ein Statement-Objekt abgewickelt werden muss. Eine
Verbesserung stellen Batch-Updates dar, die in einem Rutsch gleich eine ganze Reihe von
Daten zur Datenbank transferieren. Statt mit execute() und deren Varianten zu arbeiten,
nutzen wir die Methode executeBatch(). Damit zuvor die einzelnen Aktionen dem StatementObjekt mitgeteilt werden können, bietet die Klasse die Methoden addBatch() und clearBatch()
an. Die Datenbank führt die Anweisungen in der Reihenfolge aus, wie sie im Batch-Prozess
eingefügt wurden. Ein Fehler wird über eine BatchUpdate Exception angezeigt.
Beispiel Wir fügen einige Einträge der Datenbank als Batch hinzu. con sei unser
Connection-Objekt.
int[] updateCounts = null;
try
{
Statement s = con.createStatement();
s.addBatch( "INSERT INTO Lieferanten VALUES (x,y,z)" );
s.addBatch( "INSERT INTO Lieferanten VALUES (a,b,c)" );
s.addBatch( "INSERT INTO Lieferanten VALUES (d,e,f)" );
updateCounts = s.executeBatch();
}
catch ( BatchUpdateException e ) { }
catch ( SQLException e ) { }
Nach dem Abarbeiten von executeBatch() erhalten wir als Rückgabewert ein int-Feld
mit den Ergebnissen der Ausführung. Dies liegt daran, dass in der Batch-Verarbeitung ganz
unterschiedliche Anweisungen vorgenommen werden können und jede davon einen
unterschiedlichen Rückgabewert verwendet.
Soll der gesamte Ablauf als Transaktion gewürdigt werden, so setzen wir im try-Block den
AutoCommit-Modus auf false, damit nicht jede SQL-Anweisung als einzelne Transaktion
gewertet wird. Im Fall eines Fehlers müssen wir im catch-Block ein Rollback ausführen.
Übertragen wir dies auf das obere Beispiel, dann müssen nur die beiden Anweisungen für die
Transaktion eingesetzt werden.
try
{
con.setAutoCommit( false );
Statement s .....
...
}
catch ( BatchUpdateException e )
{
con.rollback();
}
Seite 53 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Aufgabe:
Vervollständigen Sie den Quelltext, es sollen alle Daten der Tabelle Auto selektiert werden
und als Komma-separierte Liste ausgegeben werden.
import java.sql.*;
import de.atbits.database.ConnectionHelper;
public class MeineDBAnwendung {
public static void main(String[] args) {
Connection dbConnection = null;
try {
dbConnection = ConnectionHelper
.getConnection(ConnectionHelper.MYSQL);
// printing some infos about connection
__________________ dbmd = dbConnection.getMetaData();
System.out.println("Connected to:" + dbmd.getURL());
System.out.println("Driver:" + dbmd.getDriverName());
System.out.println("Version:" + dbmd.getDriverVersion());
System.out.println("Username:" + dbmd.getUserName());
// selecting all data from Table Auto
PreparedStatement pst = dbConnection
.prepareStatement("SELECT * FROM AUTO");
ResultSet rs = pst.executeQuery();
__________________ rsMD = rs.getMetaData();
int columnCount = rsMD.__________________();
while (rs.next() != false) {
for (int counter = 1; counter <= ____________; ++counter) {
System.out.print(rs.getString(____________));
if (____________ < _____________) {
System.out.print(",");
} else {
System.out.println();
}
}
}
// close all db-ressources
________.close();
________.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (dbConnection != null) {
try {
________________.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
Seite 54 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.8 Fehlerbehandlung
6.8.1 Allgemein
Die bisher beschriebenen execute- get-, set- und update-Methoden erzeugen in Fehler- oder
Ausnahmesituationen Instanzen der Klassen
SQLException
oder
SQLWarning
oder DataTruncation
•
SQLException Die Klasse SQLException ist Basisklasse aller JDBC-Exceptions. Sie
enthält über den Fehler die folgenden Informationen: eine Fehlerbeschreibung sowie
eine weitere Beschreibung, die den XOPEN SQL-Status (beschrieben in der SQLSpezifikation) angibt, und zuletzt eine zusätzliche Ganzzahl, die vom Datenbanktreiber
kommt.
•
SQLWarning Die Klasse SQLWarning beschreibt keine kritischen Fehler. Es ist auch
keine Exception, die geworfen wird, sondern eine Warnung, die der Programmierer
explizit holen muss. Dazu dienen die Funktionen getWarnings() der Klassen
Connection, ResultSet und Statement. Werden die Meldungen nicht geholt, dann
werden sie mit Connection, ResultSet oder Statement überschrieben.
•
DataTruncation Die DataTruncation-Klasse ist ein spezieller Typ einer SQLWarnung. Sie wird immer dann erzeugt, wenn Daten während der Schreib- oder LeseOperationen verloren gingen. Die Meldung wird genauso geholt wie SQLWarning
(mittels getWarning), nur muss dann, um das Ergebnis zu erfahren, mittels instanceof
DataTruncation überprüft werden, ob es sich um DataTruncation handelt. Dies
erfordert eine Typumwandlung (Cast) von SQLWarning auf DataTruncation.
Seite 55 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Folgende Arten von Informationen stehen zur Verfügung:
•
SQL State: Methode String getSQLState() liefert einen 5 Zeichen umfassenden
Fehlercode
•
Message: Methode String getMessage() liefert eine Detailbeschreibung des
aufgetretenen Fehlers
•
Vendor Code: Methode int getErrorCode() liefert den DBMSHerstellerspezifischen Fehlercode
0
<0
>0
erfolgreiche Durchführung
Fehler aufgetreten
Ausnahme aufgetreten
Die SQLExceptions werden wie in Java üblich als Exceptions geworfen und mittels try /
catch / finally verarbeitet
Beispiel: (MS-Access Datenbank)
Die fehlerhafte Query „SELECT Firma FROM Kunden WHERE Ort =“ verursacht einen
Fehler (SQLException).
SQLState
Error Message
Vendor Code
37000
Syntaxfehler (fehlender Operator) in Abfrageausdruck „Ort =“
-3100
Achtung !!!
Zu einem Fehler können mehrere Exceptions bzw. Warnings existieren. Diese sind
hintereinander verkettet.
Bei der Abarbeitung der Exceptions bzw. Warnings muß dies berücksichtigt werden.
Seite 56 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.8.2 Abfangen von Fehlern und Ausnahmen
•
SQLException wird mit throw-Klausel geworfen. D.h. JDBC-Aufrufe müssen in
einem try-Block stehen
•
Programmblock wird bei der Methode javaüblich abgebrochen, die die Exception
auslöst
•
Abfangen der Exception und Behandlung des Fehlers erfolgt javaüblich im catchBlock (evtl. Abarbeitung mehrerer Exceptions!)
•
Für die Behandlungsreihenfolge mehrerer catch-Blöcke gilt: Spezieller Fall vor
allgemeinem Fall (wie immer in Java)
•
Finally-Block: Schließen von ResultSet und Statement
Faustregel: Schließen Sie immer im finally Alle offenen ResultSet’s, Statement’s und
Connection’s
static public void
Connection con
Statement stmt
ResultSet rs =
main (String args[]) {
= null;
= null;
null;
try {
DriverManager.registerDriver (
new oracle.jdbc.driver.OracleDriver ());
String url = "jdbc:oracle:thin:@<database>";
con = DriverManager.getConnection (
url, “Scott”, “Tiger”);
}
catch (SQLException ex) {
while (ex != null) {
System.err.print(“Fehler beim Einloggen:”);
System.err.println(ex.getMessage());
ex = ex.getNextException();
}
return;
}
// Query absetzen
try {
stmt = con.createStatement();
rs = stmt.executeQuery( “...” );
}
catch (SQLException ex) {
System.err.println(“Fehler beim Absetzen des SELECT Statements:“);
while (ex != null) {
System.err.println(“Vendor-Code: “+ ex.getErrorCode());
System.err.println(“SQLState: “+ ex.getSQLState());
System.err.println(“Error-Message: “+ ex.getMessage());
ex = ex.getNextException();
}
return;
}
Seite 57 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.8.3 Abfragen von Warnungen
•
Ausnahmen werden nicht mit dem throw-Befehl aufgerufen
•
SQLWarning betrifft ein Statement oder einen Cursor und wird dort jeweils
„angehängt“
•
Zugriff auf Warning mit SQLWarning getWarnings() und void
clearWarnings()
private static boolean checkForWarning (SQLWarning warn)
throws SQLException {
boolean rc = false;
if (warn != null) {
rc = true;
System.err.println(“+++++ WARNINGS +++++“);
while (warn != null) {
System.err.println(“Vendor-Code: “+ warn.getErrorCode());
System.err.println(“SQLState: “+ warn.getSQLState());
System.err.println(“Error-Message: “+ warn.getMessage());
warn = warn.getNextWarning();
}
}
return rc;
}
try {
stmt = con.createStatement();
rs = stmt.executeQuery( “...” );
checkForWarning(rs.getWarnings()); }
catch { ... }
try {
while (!checkForWarning(rs.getWarnings()) && rs.next())
{
System.out.println(...);
...
}
}
Seite 58 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.9 Verbindungsabbau
Connection-, Statement- und ResultSet-Objekte sollten am Ende einer DatenbankSitzung bzw. am Ende der Verarbeitung ihre Verbindung zur Datenbank explizit abgebrochen
werden.
Wird eine Connection abgebrochen, so erlischt auch die Verbindung aller Statements
dieser Connection und auch aller ResultSets, sauberer ist es jedoch auch diese explizit
zu schliessen
Beispiel:
private static closeConnection (Connection conn) {
if(conn != null) {
try { conn.close(); }
catch (SQLException sqlEx) { // Fehlerbehandlung }
}
}
try { // Verbindungsaufbau, Queries, Verarbeitung }
catch (SQLException sqlEx)
{ // Fehlerbehandlung
...
return; // Abbruch!
}
finally { // Wird auf jeden Fall ausgeführt
closeConnection (con)
}
6.10 Transaktionssteuerung
Das allgemeine Prinzip von Transaktionen bei Datenbanken wird an dieser Stelle
vorausgesetzt.
Transaktionen sind für Datenbanken ein sehr wichtiges Konzept, denn nur so bleibt die
Integrität der Daten erhalten. Transaktionen sind vergleichbar mit einer atomaren Ausführung
bei Threads, mit dem Unterschied, dass die Datenbank inmitten einer gescheiterten
Transaktion die bisher veränderten Werte rückgängig macht.
In der Standardverarbeitung in JDBC wird jede SQL-Anweisung für sich als Transaktion
abgearbeitet. Dies nennt sich Auto-Commit. Um jedoch eine Folge von Anweisungen in einer
Transaktion auszuführen, muss zunächst das Auto-Commit zurückgesetzt werden. Dann
werden die Datenbankmanipulationen ausgeführt, und die Transaktion kann anschließend
abgeschlossen (commit) oder zurückgesetzt (rollback) werden.
Beispiel Datenbankoperationen sollen in einer Transaktion ausgeführt werden.
con.setAutoCommit( false );
// Datenbankmanipulationen vornehmen
con.commit();
con.setAutoCommit( true ); // Standard wiederherstellen
Seite 59 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.11 Fazit
Ausgehend von den anfänglichen Fragestellungen ist Folgendes zusammenzufassen:
1. Wie verwendet das Anwendungsprogramm die Funktionalität der Datenbank?
Durch Klassen aus dem Package java.sql.* und entsprechende Methodenaufrufe..
2. Wie findet der Datenaustausch zwischen Anwendung und Datenbank statt?
Durch Klassen, die vom JDBC-Teiber erzeugt, und in Form von Objektinstanzen
innerhalb der Anwendung zur Verfügung gestellt werden.
3. Wie wird eine Ergebnismenge der Datenbank an die Anwendung übergeben?
Das Cursor-Konzept erlaubt es der Anwendung mit den mengenorientierten Daten der
Datenbank umzugehen. Cursor stehen in Form von Objekten der Klasse ResultSet
zur Verfügung.
4. Zu welchem Zeitpunkt findet welche Aktion der Datenbank statt?
Die Reihenfolge der Aktionen, die auf der Datenbank ausgeführt werden erledigt der
JDBC-Treiber, und ist für die Anwendung nicht relevant. Innerhalb der Anwendung
muss lediglich die richtige Reihenfolge bei der Erzeugung von Objektinstanzen und
dem Aufruf entsprechender Zugriffsmethoden eingehalten werden.
5. Welche Art von Kopplung entsteht zwischen Anwendungsprogramm und
Datenbank?
Die Kopplung findet ausschließlich über die Verwendung der standardisierten
Schnittstelle (Klassen, Methoden) aus dem Package java.sql statt.
Seite 60 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
6.12 Erweiterte Themen
• Kern von JDBC soll einfach und klein sein
• Standard-Erweiterungen für JDBC werden in die Standard Extension API ausgelagert.
• 3 wichtige Standard-Erweiterungen für JDBC
i. Java Naming and Directory Interface
• Verwendung von Datenquellen: DataSource
ii. Connection Pooling
• Wiederverwendung von Verbindungen spart Zeit
6.12.1 JNDI & Connection Pooling
Die Arbeit mit dem DriverManager sah bisher so aus, dass er mit der genauen
Datenquelle parametrisiert wurde. Es musste also immer der Namen der Datenbank sowie
(optional) der Benutzernamen und das Passwort angeben werden. Diese feste Verdrahtung
im Quellcode ist unschön, weil Änderungen zu einer zwangsläufigen Neuübersetzung führen –
was sich allerdings mit Konfigurationsdateien verändern ließe. Trotzdem stehen die Daten in
jedem Fall auf der Client-Seite, wo sie nicht immer gut aufgehoben sind.
Besser ist eine zentrale Stelle für die Konfigurationsdaten und auch für die Datenbank.
Rüstet bspw. ein Unternehmen von MySQL auf Firebird um, so müsste für ein ClientProgramm an allen Stellen, an der der Datenbanktreiber geladen und die URL für die
Datenbank aufgebaut wird, Quellcode geändert werden. Das ist äußerst unflexibel.
Daher wurde in Java eine weitere Möglichkeit geschaffen, nämlich die Konfigurationsdaten an
einer zentralen Stelle zu hinterlegen – und das heißt in Java Zugriff über JNDI. Im zentralen
Namensdienst werden Informationen über Treibername, Datenbankname uns so weiter
abgelegt und dann zum nötigen Zeitpunkt erfragt.
Wenn sich die Datenbank einmal ändern sollte, dann muss nur an dieser zentralen Stelle eine
Änderung eingespielt werden und alle, die anschließend den JNDI-Dienst erfragen, erhalten
die neue Information.
Verbindung zu einem Datengeber (es muss nicht unbedingt eine Datenbank sein) wird über
ein DataSource-Objekt realisiert, was von der JDNI-Zentrale zu erfragen ist. Mit
getConnection() wird anschließend das Connection-Objekt besorgt, ab dieser Stelle
funktioniert es gleich, wie mit dem DriverManager.
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup( "jdbc/datenbank" );
Connection con = ds.getConnection( "benutzername", "passwort" );
Das javax.naming.Context-Objekt und dessen Methode lookup() erfragen das mit
dem Namen assoziierte Objekt vom Verzeichnisdienst. Vorher muss natürlich irgendjemand
dieses Objekt dort abgelegt – auf Neudeutsch deployen. Mit dem Verweis auf das
DataSource-Objekt können wir getConnection() aufrufen und Benutzername und
Passwort angeben.
Seite 61 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Von der Schnittstelle DataSource gibt es drei unterschiedliche Ausführungen:
• Ein Standard-DataSource-Objekt. Mindestens das muss ein JDBC-2.0 kompatibler
Treiber anbieten.
• Ein DataSource-Objekt, das gepoolte Datenbankverbindungen zulässt
(ConnectionPool-DataSource), sodass eine beendete Verbindung nicht wirklich
beendet wird, sondern nur in einen Pool zur Wiederverwendung gelegt wird. Damit er
zurückgelegt werden kann, muss die Verbindung einfach nur geschlossen werden –
ein Vorgang, der in jedem Programm mit Verbindungen zu finden sein sollte.
• Ein DataSource-Objekt für verteilte Transaktionen (XADataSource).
Das Schöne daran ist, dass der konkrete Typ verborgen bleibt, und mühelos eine Umstellung
von einer einfachen DataSource auf etwa eine ConnectionPoolDataSource
vorgenommen werden kann.
Der Administrator ist nun dafür verantwortlich, dass das DataSource-Objekt, also die
Beschreibung der Datenbank-Parameter, im Namensdienst eingetragen ist. Im Allgemeinen
macht das der Container über eine XML-Beschreibungsdatei oder über eine Gui, sodass kein
Programmieraufwand von Hand nötig ist.
Wie die JNDI-DataSource bei Tomcat von MySQL integriert werden kann, zeigt die Webseite
http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html .
Oft existieren für EJB-Server grafische Dienstprogramme, mit denen sich der JNDI-Name für
die Ressource setzen lässt. Für den Sun-Standard-EJB-Container ist das etwa das Programm
deploytool. Bei Tomcat kann der Namensserver über das admintool administriert werden.
Als alternative Möglichkeit zum Connection-Pooling bietet sich das Projekt DBCP von Apache
Jakarta Commons an (http://jakarta.apache.org/commons/dbcp/ ). Hier kann der Entwickler
das Connection-Pooling steuern und muss sich nicht auf JNDI verlassen.
Seite 62 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
7 DAPRO - Best Practices
„Fools ignore complexity, experts avoid it; geniuses remove it“ (Alan Perlis)
Ich habe mir erlaubt die 11 folgenden Punkte der Autoren dadurch zu ergänzen, dass ich
jeweils kursiv das Studienfach, das sich m.A. mit dem entsprechenden Punkt beschäftigt
angegeben habe.
11 Best Practices aus dem Buch [16] “Struts Best Practices” für qualitative hochwertige
(Web-) Anwendungen
1. Erfülle die Kundenanforderungen effizient durch solide Anforderungsspezifikationen
(SWEN)
2. Spare Geld und erhöhe die Zuverlässigkeit mit effektiver Open-Source-Software
(permanente Weiterbildung, Lesen von Fachliteratur, etc.)
3. Arbeite iterativ mit einem bewährten Prozess an der Entwicklung
(SWEN)
4. Führe Unit-Tests durch
(SWEN)
5. Verwende MVC (Model-View-Controller)
(GRABO)
6. Nutze objektorientierte Programmierung, um Wiederverwendbarkeit zu maximieren
(OOP, SWEN)
7. Verwende DAO (Data Access Objects)
(DAPRO)
8. Verwende CRUD-Ereignisse (Create-Read-Update-Delete)
(DAPRO)
9. Verwende CMA (Container Managed Authorization)
(INET2, Middleware)
10. Führe Belastungstests durch
(SWEN)
11. Verwende Qualitätssicherung
(SWEN)
Arbeiten Sie an einem Projekt? – „Machs nur einmal und gleich richtig“
Neun Schritte zu einem erfolgreichen (Web-)Projekt:
1. Bauen Sie ein (HTML-) Mockup
2. Erstellen Sie die JSP’s / JSF’s
3. Entwicklung von Page-Flow, Tiles, Navigation und Stil
4. Beans-Prototyp
5. Nur-Lesen Version
6. CRUD-Version
7. Validierung
8. Sicherheit
9. Kosmetik, z.B. Drop-Down-Elemente
Seite 63 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
7.1 DAO – Data Access Objects
Die Datenzugriffsschicht, das „M“ in „MVC“. Vergeuden Sie keine wertvolle Zeit und wertvolles
Entwicklerwissen für das Schreiben unnötig komplexer Datenzugriffe. Die Wahl einer soliden
Implementierung für den Datenzugriff ist entscheiden, da er in der Regel die Basis der
gesamten Anwendung bildet.
Wackelt die Basis, wackelt auch das Haus, das Sie darauf aufbauen!
Das MVC-Pattern bedeutet eine strikte Trennen von Datenhaltung (Modell),
Datenverarbeitung und Logik (Controller) und Darstellung (View).
Das bedeutet das SQL- oder datenbankbezogener-Code nichts in Klassen zu suchen hat, die
der Darstellung oder der Verarbeitung von Daten dienen – Ein solcher Code sollte immer in
separate Klassen gekapselt werden!
Hierzu bietet sich das Entwurfsmuster Data Access Objects an. „Einer der Hauptgründe für die
Verwendung des Musters, ist es, die Datenzugriffsimplementierung vor der Geschäftslogik, die
sie nutzt, zu verbergen bzw. zu kapseln.
Die Verwendung des DAO-Patterns bietet verschiedene Vorteile. Erstens können die
Datenzugriffsmethoden standardisiert werden, und zawr ungeachtet der Datenquelle (z.B.:
eine SQL-Datenbank, ein flache Datei, XML, eine Message-Queue oder Legacy-Systeme).
Entwickler können den Datenzugriff viel leichter pflegen, wenn er standardisiert ist. Das trifft
auch auf Situationen zu, in denen alle Daten in einer relationalen Datenbank abgelegt sind.“
(vgl. [16])
Wenn die Anwendung zu einem späteren Zeitpunkt auf eine andere Datenbank umgestellt
werden soll, so müssen Sie lediglich die DAO-Klassen anpassen. Durch Verbergen der
Datenzugriffsimplementierung können Sie diese später ändern, ohne die Geschäftslogik
abändern zu müssen.
Die normalen Java-Beans (POJO = Plain Old Java Object) sollten nicht wissen, woher ihre
Daten kommen und wohin sie gehen. Das wird durch eine getrennte DAO-Klasse, mit der die
Bean kommuniziert, sichergestellt.
Eine klassische Java-Bean verfügt nur über öffentliche (public) Getter- und Setter-Methoden
für ihre Properties und ansonsten über nicht viel mehr.
Die Kopplung der Bean mit ihrer DAO-Klasse sollte nur lose über ein Interface erfolgen, so
können mehrere Klassen dasselbe Interface implementieren und für dieselbe Bean-Klasse
unterschiedliche Datenzugriffsmethoden implementieren, was wiederum den Zugriff auf das
DAO-Objekt über eine Factory ermöglicht.
Das DAO-Pattern ist vor Allem ein Entwurfsmuster, es gibt nicht konkret die Methoden vor, die
ein Interface benutzen soll. Sie können also sowohl Interface-Namen als auch die InterfaceMethoden frei wählen.
Seite 64 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
7.1.1 Allgemein:
(DAO-Klassendiagramm: aus [18])
(DAO-Sequenzdiagramm: aus [18])
Seite 65 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
BusinessObject
Das BusinessObject repräsentiert den Daten-Client. Das Objekt benötigt Zugriff auf die
DataSource um Daten zu laden und zu speichern.
DataAccessObject
Das DataAccessObject ist das Hauptobjekt dieses Entwurfsmusters. Das DataAccessObject
abstrahiert von der unterliegenden Datenzugriffsstrategie für das BusinessObject, um einen
transparenten Zugriff auf die Datenquelle zu gewährleisten.Das BusinessObject delegiert
ausserdem das Laden und die Speicherung der Datenan das DataAccessObject.
DataSource
Repräsentiert eine Datenquellen-Implementierung, das kann ein RDBMS, OODBMS, XML
Repository, das Dateisystem, etc. sein. Eine Datenquelle kann auch ein anderes System sein
(legacy/mainframe), ein Dienst (B2B Service oder Kreditkarten-Dienst), oder eine andere Art
Repository (LDAP).
TransferObject
Repräsentiert ein Transfer Object, das als Datenträger verwendet wird. Das
DataAccessObject kann das Transfer Object verwenden, um Daten zum Client zu übertragen.
Das DataAccessObject kann auch dazu verwendet werden, um Daten vom Client an ein
TransferObject zu übertragen, um die Daten in der Datenquelle zu aktualisieren.
Factory for Data Access Objects Strategy
Das DAO Entwurfsmuster kann hochflexibel umgesetzt werden, indem es mit einem der
Pattern Abstract Factory [GoF] oder der Fabrik-Methode [GoF] kombiniert werden.
(Implementierung der Factory für die DAO Strategie durch Verwendung einer Fabrikmethode aus [17])
Seite 66 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
(Implementierung der Factory für dieDAO Strategie durch Verwendung einer Abstrakten Fabrik aus [17])
7.1.2 Am Beispiel: Kundeninformation
(Implementierung des DAO Entwurfsmusters aus [17])
Seite 67 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Eine typisches DAO-Interface sieht so aus:
// Interface that all CustomerDAOs must support
public interface CustomerDAO {
public int insertCustomer(...);
(...);
public boolean deleteCustomer(...);
(...);
public Customer findCustomer(...);
(...);
public boolean updateCustomer(...);
(...);
public RowSet selectCustomersRS(...);
(...);
public Collection selectCustomersTO(...);
(...);
...
}
Die hierzu passende konkrete DAO-Implementierung für Cloudscape:
// Cloudscape concrete DAO Factory implementation
import java..sql.*;
.*;
public class CloudscapeDAOFactory extends DAOFactory {
public static final String DRIVER=
=
"COM.cloudscape.core.RmiJdbcDriver";;
public static final String DBURL=
=
"jdbc:cloudscape:rmi://localhost:1099/CoreJ2EEDB";;
// method to create Cloudscape connections
public static Connection createConnection()
() {
// Use DRIVER and DBURL to create a connection
// Recommend connection pool implementation/usage
}
public CustomerDAO getCustomerDAO()
() {
// CloudscapeCustomerDAO implements CustomerDAO
return new CloudscapeCustomerDAO();
();
}
public AccountDAO getAccountDAO()
() {
// CloudscapeAccountDAO implements AccountDAO
return new CloudscapeAccountDAO();
();
}
public OrderDAO getOrderDAO()
() {
// CloudscapeOrderDAO implements OrderDAO
return new CloudscapeOrderDAO();
();
}
...
}
Seite 68 von 104
Dipl.-Ing. (FH) D. Schiffer
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Und die DAO-Factory:
// Abstract class DAO Factory
public abstract class DAOFactory {
// List of DAO types supported by the factory
public static final int CLOUDSCAPE = 1;;
public static final int ORACLE = 2;;
public static final int SYBASE = 3;;
...
// There will be a method for each DAO that can be
// created. The concrete factories will have to
// implement these methods.
public abstract CustomerDAO getCustomerDAO();
();
public abstract AccountDAO getAccountDAO();
();
public abstract OrderDAO getOrderDAO();
();
...
public static DAOFactory getDAOFactory((
int whichFactory)) {
switch (whichFactory)) {
case CLOUDSCAPE::
return new CloudscapeDAOFactory();
();
case ORACLE
:
return new OracleDAOFactory();
();
case SYBASE
:
return new SybaseDAOFactory();
();
...
default
:
return null;
null;
}
}
}
Seite 69 von 104
Dipl.-Ing. (FH) D. Schiffer
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
7.1.3 Vorteile
Ermöglicht Transparenz:
Das Business Objekt kann die Datenquelle benutzen ohne spezifische Details über sie oder
ihre Implementierung zu wissen. Der Zugriff ist transparent, weil die Implementierungs-Details
innerhalb des DataAccessObjekts gekapselt und versteckt sind.
Enables Easier Migration
Ein DAO-Layer macht es einfacher eine Applikation von einer auf eine andere Datenquelle zu
migrieren. Die Business-Objekte haben keinerlei Kenntnis von der unterliegenden
Datenschicht. Daher betrifft der Wechsel der Datenquelle lediglich den DAO-Layer. Weiterhin
ist es möglich, wenn ein Factory-Pattern verwendet wird, eine konkrete FactoryImplementierung für jede Datenquelle zu implementieren. In diesem Fall muss für die
Migration auf eine neue Datenquelle lediglich eine weitere konkrete Fabrik implementiert
werden.
Reduzierung der Code-Komplexität in den Business-Objekten
Weil die DAO’s die gesamte Komplexität des Datenzugriffs kapseln, wird der Code in den
BusinessObjects vereinfacht. Aller datenbankspezifische Code in der Applikation (wie SQL)
wird in den DAO’s und nicht den BusinessObjekten gekapselt. Das verbessert die Les- und
Wartbarkeit des Codes.
Zentralisiert den Datenzugriff in einem separaten Layer
Weil alle Daten-Zugriffs Operationen an DAO’s delegiert werden, kann die separate
Datenzugriffsschicht als die Ebene betrachtet werden, die den Rest der Appliaktion vom
Datenzugriff entkoppelt. Dies macht die Applikation einfacher verwaltbar.
Natürlich muss diese extra schicht designed und entwickelt werden, die Vorteile überwiegen
jedoch den teilweisen Mehraufwand.
Bei Verwendung eine Factory-Strategie erhöht sich der Design und Implementierungsaufwand
nochmals, im Gegenzug erhält man die o.g. Vorteile.
Weiterführendes auch:
• http://www.torsten-horn.de/techdocs/sw-patterns.htm
• http://www.corej2eepatterns.com/Patterns2ndEd/DataAccessObject.htm
• http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
Seite 70 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Überblick über gängige Entwurfsmuster im Geschäftsapplikationsumfeld:
Seite 71 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
8 Embedded SQL
8.1 Allgemein
•
Datenbank-Zugriffe werden in die Programmiersprache eingebettet, indem eine
spezielle Syntax verwendet wird. Gewissermaßen wird die Programmiersprache um
weitere Sprachkonstrukte erweitert.
•
Variablen der Programmiersprache können auch für die Datenbank verwendet werden,
indem sie als solche deklariert werden. Innerhalb von Datenbank-Statements werden
diese Variablen mit einem Doppelpunkt „:“ als solche gekennzeichnet.
•
Quellen mit embedded SQL sind keine gewöhnlichen Quellen. Beispielsweise könnte
ein C-Compiler normalerweise keine SQL-Sprachkonstrukte übersetzen. Ein spezieller
Präprozessor muß diese Konstrukte verarbeiten.
8.2 Beispiel
#include <stdio.h>
EXEC SQL INCLUDE SQLCA;
void main( void )
{
EXEC SQL BEGIN DECLARE SECTION;
char Author[81];
char Title[81];
char ISBN[14];
EXEC SQL END DECLARE SECTION;
EXEC SQL CONNECT TO "azamon" USER "dba" IDENTIFIED BY "sql";
strcpy( ISBN, "0-87930-480-4" );
EXEC SQL SELECT Author, Title, ISBN
INTO :Author, :Title, :ISBN
FROM Books
WHERE ISBN = :ISBN;
printf( "%s: %s ISBN %s\n", Author, Title, ISBN );
EXEC SQL DISCONNECT ALL;
}
Seite 72 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
8.3 Übersetzung eines Programms mit embedded SQL
geschieht im Wesentlichen durch folgende 5 Schritte:
1. Programmquelle wird durch den SQL Präprozessor verarbeitet. Der Präprozessor
analysiert die Quelle und bearbeitet das eingebettete SQL (Anweisungen die mit
„EXEC SQL“ beginnen).
2. Präprozessor erzeugt 2 Ausgabedateien: Eine Ausgabedatei, in der die Aufrufe von
„EXEC SQL“ durch Funktionsaufrufe der jeweiligen Datenbank ersetzt wurden. Die
zweite Datei ist ein sogenanntes Datenbank Abfrage Modul (nicht bei allen DBn).
3. Die vorkompilierte C-Programmquelle wird wie jede andere C-Quelle kompiliert.
4. Der Linker verwendet die Objekt-Datei und bindet sie zusammen, insbesondere auch
mit den Libraries, die Bestandteil der Datenbank-Software sind.
5. Das Datenbank Abfrage Modul (falls verwendet) wird mit einem speziellen BindProgramm der Datenbank übergeben. Bei Ausführung des Statements wird dieses
übergebene Modul aufgerufen.
8.4 Datenaustausch zwischen Programm und Datenbank
•
Die Kommunikation zwischen Programm und Datenbank wird über Hostvariablen
durchgeführt. Diese werden im Anwendungsprogramm deklariert und tragen innerhalb
der eingebetteten SQL-Statements einen führenden „:“ (Doppelpunkt).
•
Diese Variablen heißen Hostvariablen, weil sie in der umgebenden (Host-)Sprache
definiert sind, d.h. die Datenbank verwendet direkt Speicher, den die Anwendung
alloziert hat.
•
Die Zuordnung zwischen Datentypen in C und SQL wird durch den SQL-Standard
geregelt. Er legt in einer Tabelle fest, welche Datentypen sich entsprechen.
Seite 73 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
8.5 Cursorkonzept
Bewältigung der Diskrepanz zwischen
•
mengenorientierten Datenbanken und
•
objekt- bzw. recordorientierten Programmiersprachen
Was ist zu tun, wenn eine Abfrage nicht nur eine Zeile, sondern eine Menge von Zeilen liefert?
Folgendes Beispiel demonstriert den Einsatz von Cursor in diesem Zusammenhang:
#include <stdio.h>
EXEC SQL INCLUDE SQLCA;
void main( void )
{
EXEC SQL BEGIN DECLARE SECTION;
char Author[81];
char Title[81];
char ISBN[14];
EXEC SQL END DECLARE SECTION;
int NochDatenDa = TRUE;
EXEC SQL CONNECT TO "azamon" USER "dba" IDENTIFIED BY "sql";
/* Cursor deklarieren */
EXEC SQL DECLARE C1 CURSOR FOR
SELECT Author, Title, ISBN
FROM BOOKS;
/* Cursor oeffnen */
EXEC SQL OPEN C1;
/* Cursor ueber Ergebnismenge bewegen */
while ( NochDatenDa )
{
EXEC SQL FETCH C1 INTO :Author, :Title, :ISBN;
if ( SQLCODE == SQLE_NOTFOUND )
NochDatenDa = FALSE;
else
printf( "%s: %s ISBN %s\n", Author, Title, ISBN );
}
/* Cursor schliessen */
EXEC SQL CLOSE C1;
EXEC SQL DISCONNECT ALL;
}
Seite 74 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
8.6 Statisches und dynamisches SQL
Der Unterschied zwischen statischem und dynamischem SQL besteht in erster Linie aus dem
Zeitpunkt, zu dem die SQL-Anweisung feststehen muß.
•
Statisches SQL steht bereits zum Zeitpunkt der Übersetzung des Programms fest
•
Dynamisches SQL wird zur Laufzeit konstruiert.
Beispiel für dynamisches SQL:
#include <stdio.h>
EXEC SQL INCLUDE SQLCA;
void main( void )
{
EXEC SQL BEGIN DECLARE SECTION;
char Stmt[200];
EXEC SQL END DECLARE SECTION;
strcpy( Stmt, "update Books set Title = ’DDJ Database
Development’"
"where ISBN = ’0-87930-480-4’" );
EXEC SQL CONNECT TO "azamon" USER "dba" IDENTIFIED BY "sql";
EXEC SQL EXECUTE IMMEDIATE :Stmt;
EXEC SQL DISCONNECT ALL;
}
In diesem Beispiel ergeben sich gegenüber statischem SQL folgende Nachteile:
•
Die Optimierung des Statements kann erst zur Laufzeit durchgeführt werden
•
Der Vorgang des Optimierens würde bei jeder Ausführung des Statements neu
erfolgen müssen
•
D.h. es sind Laufzeiteinbußen mit jeder Ausführung des Statements hinzunehmen
Seite 75 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
8.7 Kopplung zwischen Anwendung und Datenbank
1. Embedded SQL bietet Datenbankunabhängigkeit auf Nivau der Programmquelle.
2. Bei Verwendung eines neuen Datenbanksystems ist eine erneute Übersetzung des
Programms notwendig, da das Programm Aufrufe einer proprietären DatenbankBibliothek enthält.
3. Gleichzeitiger Zugriff auf verschiedene Datenbanksysteme ist sehr mühsam.
4. Veränderungen an der Struktur der Datenbank haben i.d.R. Auswirkungen auf das
Anwendungsprogramm.
5. Veränderungen im Anwendungsprogramm (z.B. Wechsel der Programmiersprache)
können drastische Auswirkungen haben.
8.8 Fazit
Ausgehend von den anfänglichen Fragestellungen ist folgendes zusammenzufassen:
1. Wie verwendet das Anwendungsprogramm die Funktionalität der Datenbank?
Durch die Einbettung von SQL-Anweisungen in die Programmiersprache der
Anwendung, als wäre SQL eine Erweiterung der Programmiersprache.
2. Wie findet der Datenaustausch zwischen Anwendung und Datenbank statt?
Durch Hostvariablen, die im Anwendungsprogramm erzeugt werden und die die
Datenbank verwenden kann.
3. Wie wird eine Ergebnismenge der Datenbank an die Anwendung übergeben?
Das Cursor-Konzept erlaubt es der Anwendung mit den mengenorientierten Daten der
Datenbank umzugehen.
4. Zu welchem Zeitpunkt findet welche Aktion der Datenbank statt?
Durch die Möglichkeit sowohl statisches als auch dynamisches SQL auszuführen,
kann fallbezogen die optimale Vorgehensweise gewählt werden.
5. Welche Art von Kopplung entsteht zwischen Anwendungsprogramm und
Datenbank?
Embedded SQL bietet Kompatibilität zu speziellen Datenbanksystemen auf
Quellniveau. D.h. soll eine Anwendung für verschiedene Datenbanksysteme
angeboten werden, muss jeweils neu compiliert und gelinkt werden.
Anwendungen sollen für verschiedene Datenbanksysteme gleichermaßen verfügbar sein.
Deshalb ist der Bedarf nach Programmierschnittstellen gegeben, die einen simultanen Zugriff
auf verschiedene Datenbanksysteme erlauben.
Seite 76 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
9 Open Database Connectivity (ODBC)
9.1 Allgemein
Initiator: Microsoft und andere
Zweck: Einheitliche API für verschiedene DBMS (API = Application Program Interface)
9.1.1 Unterschiede zu embedded SQL
•
ODBC ist ein Call-Level-Interface: Menge von Funktionen, die durch Aufruf in einem
Programm verwendet werden
•
Funktionen werden in einer Funktionsbibliothek zur Verfügung gestellt
•
Datenbankunabhängige Schnittstelle (ohne Neu-Übersetzung der Anwendung kann
diese verschiedene Datenbanksysteme ansprechen)
•
ODBC unterstützt nur dynamisches SQL
•
ODBC ist für unterschiedliche Betriebssystem-Plattformen verfügbar (Windows, Unix,
etc.)
9.1.2 Ziele
•
Entwicklersicht: Anwendungsprogramme sollen mit möglichst jeder verfügbaren
Datenbank funktionsfähig sein.
•
Datenbankhersteller: Möglichst jedes Anwendungsprogramm soll mit der eigenen
Datenbank kommunizieren könne.
•
Benutzersicht: Eine Kombination aus den beiden oben genannten Sichten. Höchste
Flexibilität durch jede erdenkliche Kombination aus Anwendung und Datenbank.
9.1.3 Design-Prinzipien
ODBC wurde insbesondere mit dem Blick auf folgende Prinzipien konzipiert:
1. ODBC ist eine API, die SQL verwendet. Dem zu Folge lässt sich prinzipiell jedes
Datenmodell anbinden, das sich in SQL ausdrücken lässt.
2. Verbindung zur Datenbank wird abstrahiert und bleibt vor der Anwendung verborgen.
Komplexe Mechanismen des Verbindungsaufbaus liegen ausserhalb der Anwendung
der Schnittstelle. Basiert auf dem Client-Server Prinzip
3. Flexibilität durch jede erdenkliche Kombination aus Anwendung und Datenbank (sogar
noch zur Laufzeit der Anwendung)
Seite 77 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
9.2 Architektur
Die Architektur von ODBC gliedert sich in vier Komponenten:
1. Das Anwendungsprogramm verwendet als Schnittstelle zur Datenquelle (Datenbank)
nur den ODBC-Treiber-Manager.
2. Der Treiber-Manager stellt der Anwendung die ODBC-Schnittstelle zur Verfügung. Die
Kommunikation zur Datenquelle übernehmen spezifische ODBC-Treiber. D.h. der
Treiber-Manager fungiert lediglich als Brücke zwischen datenbankunabhängiger
Schnittstelle und datenbankspezifischem Zugriff auf Datenquellen.
Der Treiber-Manager unter Windows liegt in Form einer DLL (odbc32.dll) vor.
3. Der Treiber wickelt die Kommunikation mit der Datenquelle ab. Diese beinhaltet das
Management der Datenbank-Verbindung, Durchführung von Datenzugriffen sowie
Fehlerbehandlung. Je ein Treiber pro DBMS.
4. Die Datenquelle ist eine Abstraktion, hinter der sich der Zugriff auf eine Datenbank
verbirgt.
Komponenten von ODBC
Seite 78 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
9.3 Vergleich zu proprietären DB-API’s
Proprietäre API:
ODBC:
Seite 79 von 104
Dipl.-Ing. (FH) D. Schiffer
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
9.4 Programmschritte beim ODBC-Zugriff
Verbindung mit der Datenquelle herstellen:
•
Zugriff auf den ODBC-Treiber-Manager: Handle auf das Environment alloziieren:
SQLAllocHandle( SQL_HANDLE_ENV )
•
Zugriff auf eine Datenquelle öffnen: Handle auf eine Datenbank-Connection alloziieren:
SQLAllocHandle( SQL_HANDLE_DBC )
•
Verbindung mit der Datenquelle herstellen:
SQLConnect( ConHandle, … )
SQL-Statement aufbauen und ausführen:
•
Zugriff auf Statement eröffnen: Handle auf Statement alloziieren:
SQLAllocHandle( SQL_HANDLE_STMT, …)
•
Statement durchführen: z.B.
SQLExecDirect( StmtHandle, „select …“)
Ergebnisse verwerten:
•
SQLFetch() bewegt den Cursor auf der Ergebnismenge
•
SQLGetData() liest ein einzelnes Feld aus der aktuellen Zeile der Ergebnismenge
•
SQLCloseCusor() schließt den Cursor wieder
Transkation beenden und Statement freigeben:
•
SQLEndTrans(…) beendet die Transaktion
•
SQLFreeHandle( SQL_HANDLE_STMT ) gibt das Statement-Handle frei
Verbindung zur Datenbank trennen und Resourcen freigeben:
•
SQLDisconnect(…) trennt Verbindung zur Datenquelle
•
SQLFreeHandle ( SQL_HANDLE_DBC ) gibt Verbindungs-Handle frei
•
SQLFreeHandle ( SQL_HANDLE_ENV ) gibt Umgebungs-Handle frei
Seite 80 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
9.5 Beispiel
// Includes
#include <iostream>
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
using namespace std;
// Deklaration der Klasse mniODBC
class mniODBC
{
public:
mniODBC( void ); // Konstruktor
˜mniODBC( void ); // Destruktor
bool Connect( char* DataSource, char* UId, char* Pwd );
void Disconnect( void );
bool AllocStmt( void );
void FreeStmt( void );
bool ExecDirect( char* Statement );
void ShowResults( void );
private:
SQLHANDLE m_EnvHandle;
SQLHANDLE m_ConHandle;
SQLHANDLE m_StmtHandle;
// Copy und Zuweisung nicht erlaubt
mniODBC( const mniODBC& Orig );
mniODBC& operator=( const mniODBC& Orig );
};
// Implementierung der Klasse
mniODBC::mniODBC( void )
: m_EnvHandle( NULL ), m_ConHandle( NULL ), m_StmtHandle( NULL
)
{
SQLRETURN rc = SQL_SUCCESS;
// Alloziere Environment Handle
rc = SQLAllocHandle( SQL_HANDLE_ENV,
SQL_NULL_HANDLE,&m_EnvHandle );
// Setze ODBC Application Version auf 3.x
if ( rc == SQL_SUCCESS )
rc = SQLSetEnvAttr( m_EnvHandle,
SQL_ATTR_ODBC_VERSION,(SQLPOINTER) SQL_OV_ODBC3,
SQL_IS_UINTEGER );
// Alloziere Connection Handle
if ( rc == SQL_SUCCESS )
rc = SQLAllocHandle( SQL_HANDLE_DBC,
m_EnvHandle,&m_ConHandle );
}
Seite 81 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
mniODBC::˜mniODBC( void )
{
// Connection Handle freigeben
if ( m_ConHandle != NULL )
(void)SQLFreeHandle( SQL_HANDLE_DBC, m_ConHandle );
// Environment Handle freigeben
if ( m_EnvHandle != NULL )
(void)SQLFreeHandle( SQL_HANDLE_ENV, m_EnvHandle );
}
bool mniODBC::Connect( char* DataSource, char* UId, char* Pwd )
{
SQLRETURN rc = SQL_SUCCESS;
// Connection herstellen
if ( m_ConHandle != NULL )
{
rc = SQLConnect( m_ConHandle, (SQLCHAR*)DataSource,
SQL_NTS,(SQLCHAR*)UId, SQL_NTS,
(SQLCHAR*)Pwd, SQL_NTS );
// Anmerkung: SQL_NTS steht fuer null terminated string
// und bedeutet, dass die Laenge durch die Stringlaenge
// gegeben ist.
if ( rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO )
return true;
}
return false;
}
void mniODBC::Disconnect( void )
{
// Connection trennen
if ( m_ConHandle != NULL )
{
(void)SQLDisconnect( m_ConHandle );
}
}
bool mniODBC::AllocStmt( void )
{
SQLRETURN rc;
// Alloziere Statement Handle
if ( m_ConHandle != NULL )
{
rc = SQLAllocHandle( SQL_HANDLE_STMT, m_ConHandle,
&m_StmtHandle );
if ( rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO )
return true;
}
return false;
}
Seite 82 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
void mniODBC::FreeStmt( void )
{
// Statement Handle freigeben
if ( m_StmtHandle != NULL )
(void)SQLFreeHandle( SQL_HANDLE_STMT, m_StmtHandle );
}
bool mniODBC::ExecDirect( char* Statement )
{
SQLRETURN rc;
// Statement ausfuehren
if ( m_StmtHandle != NULL )
{
rc = SQLExecDirect( m_StmtHandle, (SQLCHAR*)Statement,
SQL_NTS );
if ( rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO )
return true;
}
return false;
}
void mniODBC::ShowResults( void )
{
SQLRETURN rc = SQL_SUCCESS;
// Lokale Variablen fuer das Ergebnis
SQLCHAR Author[80];
SQLCHAR Title[80];
// Binde die Felder des Ergebnisses an diese Variablen
(void)SQLBindCol( m_StmtHandle, 1, SQL_C_CHAR,
(SQLPOINTER)Author, sizeof(Author), NULL);
(void)SQLBindCol( m_StmtHandle, 2, SQL_C_CHAR,
(SQLPOINTER)Title, sizeof(Title), NULL);
// Kopfzeile ausgeben
cout << "Autor: Titel" << endl << endl;
// Ergebnismenge ausgeben
while ( rc != SQL_NO_DATA_FOUND )
{
rc = SQLFetch( m_StmtHandle );
if ( rc != SQL_NO_DATA_FOUND )
cout << Author << ": " << Title << endl;
}
// Cursor schliessen
SQLCloseCursor( m_StmtHandle );
return;
}
Seite 83 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
/* -------------------------------------------------------------------Hauptprogramm
-------------------------------------------------------------------- */
void main( void )
{
bool rc;
cout << "Datenbankprogrammierung" << endl;
cout << "Select mit ExecDirect" << endl;
cout << endl;
// Instantiiere ODBC
mniODBC myDB;
// Stelle Connection her
rc = myDB.Connect( "azamon", "dba", "sql" );
if ( !rc )
{
cout << "Connection zu amazon konnte nicht aufgebaut
werden" << endl;
return;
}
// Alloziere Statement Handle
rc = myDB.AllocStmt();
if ( !rc )
{
cout << "Statement Handle konnte nicht alloziert werden"
<< endl;
myDB.Disconnect();
return;
}
// Fuehre das Statement aus
rc = myDB.ExecDirect( "select author, title from Books;" );
if ( !rc )
{
cout << "Statement konnte nicht ausgefuehrt werden" <<
endl;
myDB.FreeStmt();
myDB.Disconnect();
return;
}
// Zeige das Ergebnis an
myDB.ShowResults();
// und aufraeumen
myDB.FreeStmt();
myDB.Disconnect();
return;
}
Seite 84 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
9.6 Prinzipien von ODBC
9.6.1 Handles
Ein Handle ist ein Zugriffsmechanismus, der die konkrete Ausgestaltung eines Bestandteils
von ODBC verbirgt. Zugriffe auf Funktionen werden über Handles bereitgestellt.
Folgende Arten von Handles bilden in ODBC eine Hiercharchie:
•
Environment Handle: Globaler Kontext, in dem ODBC in einer Anwendung verwendet
wird. Repräsentiert den Zugriff auf den ODBC Treiber-Manager.
•
Connection Handle: Verwaltet alle Informationen einer Verbindung zur Datenbank.
•
Statement Handle: Wird verwendet um SQL-Anweisungen durchzuführen und die
Ergebnisse der Datenbank zu erreichen. Wird im Context eines Connection Handles
erzeugt.
Durch die Handles wird somit von den dem ODBC-Treiber zu Grunde liegenden proprietären
Funktionen / Structs / Klassen abstrahiert.
Diese Abstraktionsschicht ist bei den gängigen relationalen DBM’s (DB2, Oracle, Sybase,
PostgreSQL) relativ dünn. Alle diese DBM’s verwenden in ihren eigenen Schnittstellen dieses
auf Environment, Connection und Statement basierte Prinzip.
9.6.2 Fehlerbehandlung
ODBC bietet ein dreistufiges Konzept im Umgang mit Fehlern und Warnungen:
1. ODBC-Funktionen haben einen Returncode, der Auskunft über Erfolg oder Misserfolg
des Aufrufs gibt.
z.B. SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_ERROR
2. Die Funktion SQLError() gibt ausführlichere Auskunft, wenn ein Fehler aufgetreten
ist.
3. Die Funktionen SQLGetDiagField bzw. SQLGetDiagRec erlaubt es der Anwendung
einen kompletten Stack von Meldungen auszuwerten.
Seite 85 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
9.7 Dynamisches SQL in ODBC
ODBC kann kein statisches SQL ausführen. Dies ist die Folge der Einführung eines CallLevel-Interfaces und der damit verbundenen allgemeineren Kopplung zur Datenbank.
ODBC unterstützt diverse Formen dynamischen SQLs:
•
Direkte Ausführung: Über die Funktion SQLExecDirect() wird eine SQL-Anweisung
direkt ausgeführt.
•
Ausführung in zwei Phasen: Statement wird mit der Funktion SQLPrepare()
zunächst von der Datenbank aufbereitet. Zugriffsplan wird erstellt. Mit SQLExecute()
wird der Zugriffsplan (ggf. mehrfach) ausgeführt.
•
Stored procedures: Vor der ersten Verwendung wird auf der Datenbank eine stored
procedure erzeugt. Einen entsprechenden Zugriffsplan erstellt die Datenbank
automatisch. Beim eigentlichen Aufruf der Prozedur müssen nicht mehr alle Phasen
der Verarbeitung durchlaufen werden.
Asynchrone Ausführung
Datenbankabfragen können mitunter sehr lange dauern. Bei asynchroner Ausführung gibt
ODBC sofort nach dem Aufruf der Anweisung die Kontrolle an das Anwendungsprogramm
zurück.
Die Anwendung kann dann mittels „polling“ prüfen, ob die Datenbank mittlerweile mit der
Verarbeitung der Anweisung zu Ende gekommen ist.
Dieses Prinzip kann insbesondere bei Multithreaded-Anwendungen oder Applikationen die
mehrere Prozesse verwenden von Nutzen sein.
9.8 Retrieval von Informationen in ODBC
SQLFetch / SQLGetData
ODBC verwendet bei Statements, die Ergebnismengen liefern, das Cursor-Prinzip. In Folge
dessen muss nach einem SQLExecDirect() oder SQLExec() stets SQLFetch()
aufgerufen werden, damit der Cursor auf die erste Zeile der Ergebnismenge gerückt wird.
Mit SQLGetData können dann Daten einer Spalte innerhalb der aktuellen Cursor-Position in
eine übergebene Variable kopiert werden.
Binden von Variablen
Eine Alternative zur Verwendung von SQLGetData ist das Binden von Variablen an erwartete
Ergebnisspalten. Dies geschieht vor dem ersten Aufruf der Funktion SQLFetch.
Das Binden übernimmt die Funktion SQLBindCol.
Seite 86 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
9.9 Fazit
Ausgehend von den anfänglichen Fragestellungen ist folgendes zusammenzufassen:
1. Wie verwendet das Anwendungsprogramm die Funktionalität der Datenbank?
Durch einheitliche Funktionsaufrufe, die als Interface vom ODBC-Teiber-Manager zur
Verfügung gestellt werden.
2. Wie findet der Datenaustausch zwischen Anwendung und Datenbank statt?
Durch Datenstrukturen, die vom ODBC-Teiber erzeugt, und in Form von Handles
innerhalb der Anwendung zur Verfügung gestellt werden.
3. Wie wird eine Ergebnismenge der Datenbank an die Anwendung übergeben?
Das Cursor-Konzept erlaubt es der Anwendung mit den mengenorientierten Daten der
Datenbank umzugehen.
4. Zu welchem Zeitpunkt findet welche Aktion der Datenbank statt?
Die Reihenfolge der Aktionen, die auf der Datenbank ausgeführt werden erledigt der
ODBC-Treiber, und ist für die Anwendung nicht relevant. Innerhalb der Anwendung
muss lediglich die richtige Reihenfolge bei der Erzeugung von Handles eingehalten
werden.
5. Welche Art von Kopplung entsteht zwischen Anwendungsprogramm und
Datenbank?
Die Kopplung findet ausschließlich über die Verwendung der standardisierten
Schnittstelle des ODBC-Treiber-Managers statt. Über die Einrichtung von sog. DSNs
können somit unterschiedliche Datenbank mit derselben Anwendung angesprochen
werden.
Seite 87 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
10 ActiveX Data Objects.NET (ADO.NET)
10.1 Allgemein
Das .NET-Framework ist Microsofts zukünftige strategische Entwicklungsplattform.
ADO.NET, als direkter Technologienachfolger von ADO, enthält in diesem Zusammenhang
Klassen über die Datenzugriffsdienste bereitgestellt werden.
Das neue Datenzugriffsmodell hat Ähnlichkeiten mit dem Objektmodell von ADO,
unterscheidet sich jedoch wesentlich in seiner XML-basierten, unverbundenen Architektur.
Motivation
•
Insbesondere Web-Anwendungen benötigen eine lose Kopplung zwischen Anwendung
und Daten (auf Grund der Zustandslosigkeit)
•
XML entwickelt sich zum „universellen Datenformat“
Probleme mit bestehenden APIs
•
ADO, OLE DB und ODBC wurden für enge Kopplung und dauerhafte Verbindung
konzipiert
•
Remote Data Services (RDS) für nichtverbundene Verbindung: kein
Zustandsmanagement
Seite 88 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
10.2 Historie
Zur Anbindung von Datenbanken sind von Microsoft im Laufe der Jahre diverse Technologien
entwickelt worden, die schlussendlich zu ADO.NET geführt haben.
10.2.1 Open Database Connectivity (ODBC)
•
<Siehe vorherige Kapitel>
•
ODBC (Open Database Connectivity) bot als erste
Technologie Zugriff auf unterschiedliche relationale
Datenbanken über eine SQL-gestützte Standardschnittstelle.
Anwendungen können dank ODBC mit dem gleichen Code auf
verschiedene Datenbanken zugreifen.
•
Hierzu ist lediglich die Installation des ODBC Treibers für die
vom Anwender ausgewählte Datenbank erforderlich.
•
Der Zugriff erfolgt mittels Handels und Cursor.
•
Low-Level API eher für C/C++ Programme geeinet, auf Grund
des Handles-Konzeptes (Zeiger)
10.2.2 Data Access Objects (DAO)
•
Im Gegensatz zu ODBC stellt DAO
Objekte zur Datenbankanbindung
zur Verfügung
•
Objekte repräsentieren ein
Datenbanksystem
•
Zugriff auf DAO erfolgt über eine
Jet Engine
•
Jet Engine ist für Access
Datenbanken optimiert
•
Über ODBC kann auch auf andere
Datenbank zugegriffen werden
•
Vorteil: Einfacher zu
programmieren als mit ODBC
•
Nachteil: sehr langsamer Zugriff
(Übersetzung von
Funktionsaufrufen zwischen DAO
und ODBC)
Seite 89 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
10.2.3 Remote Data Objects (RDO)
•
Ähnliches Objektmodell wie DAO
•
Objekte können direkt auf ODBC zugreifen, ohne über Jet
Engine zu gehen
•
daher Reduzierung des Performance Verlustes gegenüber
DAO
10.2.4 OLE DB
•
Erweiterung auf nicht relationale
Datenquellen (z.B. Microsoft
Exchange Server)
•
Grundlegender COM-Baustein für
das Speichern und Auslesen von
Datensätzen
•
Einheitlicher Zugriff auf relationale
sowie nicht-relationale
unstrukturierte Datenquellen
Seite 90 von 104
Dipl.-Ing. (FH) D. Schiffer
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
10.2.5 ADO - ActiveX Data Objects
•
OLE DB ist Low-Level-Programmierschnittstelle ->
mühsam
•
Setzt auf OLE DB als einfacheres High-Level-Interface auf
•
Kombiniert Vorteile von DAO und RDO
•
Basierend auf COM-Modell (sprachübergreifend,
plattformunabhängig)
Seite 91 von 104
Dipl.-Ing. (FH) D. Schiffer
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
10.3 Das ADO.NET Objekt Modell
ADO.NET unterscheidet prinzipiell zwischen connected und disconnected Objekt Modell.
•
Data Provider für connected Zugriff
•
DataSet bietet Funktionalität für disconnected Datenobjekte
•
Auch Datenzugriffsmodell neu – bisher hat Microsoft immer nur weitere
Abstraktionsschichten über ODBC gelegt, die Basis aber beibehalten – Jetzt .NET
Provider, nicht mehr ODBC basiert.
Auf der untersten Schicht findet man den Datenspeicher. Dies kann natürlich ein DatenbankServer oder eine OLE DB Datenquelle, aber auch jede beliebige relationale oder nichtrelationale Datensammlung sein.
Der Datenspeicher ist herstellerspezifisch und im Regelfall unabhängig von .NET.
Die Brücke bildet der Managed Provider, der auf dem Datenspeicher aufsetzt und den
Anschluss an das .NET-Framework schafft. Wie überall in .NET bedeutet die Eigenschaft
„managed“ auch hier, dass. Es handelt sich also um eine Reihe von .NET-Komponenten.
Der Managed Provider umfasst die Verbindung zur Datenquelle, das Ausführen von (SQL-)
Kommandos, und den Transport der Daten zu und von der nächstfolgenden Schicht, der
Datenmenge (Data Set).
Die grundlegende Entscheidung für das unverbundene Modell in ADO.NET bedeutet an dieser
Stelle, dass der Datentransport gebündelt und explizit abläuft und nicht kontinuierlich und
automatisch wie bei einer verbundenen Datenmenge.
Beim Ausführen eines SELECT-Kommandos wird eine Verbindung zur Datenquelle
hergestellt, die Abfrage abgesetzt, die Ergebnismenge komplett zum Client transportiert und
die Verbindung dann wieder geschlossen. Das Ändern von Daten geschieht ganz analog:
Seite 92 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Verbindung öffnen, eine Reihe von UPDATEs, INSERTs und DELETEs absetzen, Verbindung
wieder schließen.
Daraus wird klar, dass an die dritte
Schicht des Modells (DataSet) hohe
Anforderungen gestellt werden müssen.
Die Datenmenge verhält sich für den
Anwender wie eine eigenständige kleine
"offline-" Datenbank. Sie speichert die von
der Datenquelle abgefragten
Informationen, bietet sie zur
Weiterverarbeitung (z.B. in
Steuerelementen) an und registriert und
hält Änderungen bis zur Aktualisierung in
der Datenquelle.
Auf der obersten Schicht sitzen alle
Komponenten für die Anzeige und
Bearbeitung der in der Datenmenge
gespeicherten Informationen. Das
Datengitter (Data Grid) ist der klassische
Vertreter für diese Gattung von
Komponenten. Aber natürlich gehören
auch alle anderen Steuerelemente mit
Datenanschluss dazu und überhaupt alle
Komponenten, die Daten aus Data Sets
auslesen und/oder manipulieren.
Seite 93 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
10.3.1 Managed Provider
Sind eine Sammlung von Klassen,
die den Zugriff auf Datenquellen
regeln, der Zusatz „managed“ liefert
uns einen Hinweis darauf, dass
diese Provider ebenfalls in .NETManaged-Code geschrieben sind
und daher in der gemeinsamen
Laufzeitumgebung (CLR = Common
Language Runtime) laufen.
.NET arbeitet wie COM sehr stark
mit Schnittstellen, so dass in diesem
Objektmodell hauptsächlich
Beziehungen zwischen Interfaces
dargestellt sind.
Aufbau eines Managed Providers
Das Objektmodell stellt somit allgemeine Beziehungen dar, die für jeden beliebigen Managed
Provider gelten. Alle aufgeführten Schnittstellen sind im Namensraum System.Data deklariert.
Mit IDbConnection und IDbCommand enthält ein Managed Provider zwei von ADO her
bekannte Komponenten.
Die Interfaces / Klassen / Komponenten eines Managed Provider:
•
DBConnection / IDbConnection: Herstellung einer Verbindung zur Datenquelle
•
DBCommand / IDbCommand: Zum Ausführen der Befehle
•
DataReader: Liefert die Ergebnisse einer Abfrage
•
DataAdapter: Verbindet das DataSet mit der Datenquelle, Updates
Beispiele für DataProvider:
•
SQL Server .NET Data Provider
- SQL Server 2000, SQL Server 7, MSDE
- Verwendet eigenes Kommunikationsprotokoll (TDS)
- Zugriff via System.Data.SQL Namespace
•
OleDb .NET Data Provider
- SQLOLEDB – OLE DB provider für SQL-Server
- MSDAORA – OLE DB provider für Oracle
- Microsoft.Jet.OLEDB.4.0 für Microsoft Jet
- Zugriff via System.Data.OleDb Namespace
•
ODBC .NET Data Provider
Seite 94 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
10.4 Connection Objekt
•
Repräsentiert eine Verbindung zu einer Datenbank
•
enthält eine Eigenschaft für den ConnectionString und den aktuellen Zustand
(offen/geschlossen) sowie
•
Verbindung zur Datenquelle ändern - Methoden (create, open, close)
•
Transaktionen handhaben (begin, commit, abort)
Beispiel:
string cString = "user id=sa;" +"password=;" +
"database=northwind;" +
"data source=MyComputerName\\NetSDK;" +
"connect timeout=5";
// connect to SQL Server
SqlConnection myConnection = new SqlConnection(cString);
/* string cString2 = "Provider=Microsoft.Jet.OLEDB.4.0;" +
"data source=Provider.mdb";
OleDbConnection myConnection = new OleDbConnection(cString2); */
myConnection.Open();
// do s.th
myConnection.Close();
10.5 Command Objekt
•
Repräsentiert eine Abfrage (Query) einer Datenquelle
•
enthält einen Verweis auf die zugehörige Datenbank-Verbindung und einen String, der
das auszuführende Kommando aufnimmt. Im Unterschied zu ADO, muss in ADO.NET
jede Ausführung über ein DbCommand-Objekt ablaufen, die direkte Ausführung z.B.
über die Verbindung selbst wird nicht mehr unterstützt
•
enthält zwei Methoden zum Ausführen von Befehlen. Mit ExecuteNonQuery() wird ein
UPDATE, INSERT oder DELETE ausgeführt, mit anderen Worten jedes Kommando,
das keine Ergebnis-Menge zurückliefert. Wenn ein SELECT-Statement ausgeführt wird
oder ein anderer Befehl, der Daten zurückliefert (ADO.NET ist ja nicht auf SQL
beschränkt), benutzt man ExecuteReader() und erhält eine von DataReader
abgeleitete Klasse zurück, die den Zugriff auf die Ergebnismenge erlaubt
Wichtige Properties
•
ActiveConnection: Verbindung zur Datenquelle
•
CommandText: Enthält Query String
•
CommandType: Wie ist Command Text zu interpretieren (SQL, StoredProcedure,
etc.)
•
CommandTimeout: Sekunden bis zum Timeout
Seite 95 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
• RecordsAffected: Anzahl der betroffenen Datensätze
Zur Ausführung von SQL-Anweisungen stellt das Command-Objekte folgende Methoden zur
Verfügung:
ExecuteNonQuery
ExecuteScalar
ExecuteReader
Führt Befehle wie die Transact-SQL-.Anweisung INSERT, UPDATE
und SET aus. Dabei wird keine Ergebnsimenge zurückgegeben
Liefert einen einzigen Wert zurück.
z.b. „SELECT COUNT (*) FROM ORDERS“
Liefert ein SqlDataReader-Objekt zurück. In diesem Objekt stehen
die Ergebnisreihen der Abfrage.
Führt Befehle aus, die Zeilen zurückgeben.
Beispiel: ExecuteNonQuery:
string conStr = "CREATE TABLE Abomodell " +
"(AMNr LONG NOT NULL, Name CHAR(20) NOT NULL, " +
"Grundgebuehr CURRENCY, Zeitgebuehr CURRENCY, " +
"FreiStd LONG)";
// OleDBCommand Objekt anlegen
OleDbCommand cmd = new OleDbCommand(conStr, conn);
// Bei Kommandoausführung werden keine Daten zurückgegeben
Int32 RowsAffected = cmd.ExecuteNonQuery();
Beispie: ExecuteScalar
// OleDBCommand Objekt anlegen
OleDbCommand cmd = new OleDbCommand(
"SELECT Name FROM Abomodell " +
"WHERE AboID = @Abo_ID", conn);
//Übergabeparameter wird erzeugt und initialisiert
cmd.Parameters.Add("@Abo_ID", aboID);
Name wird zurückgegeben
string aboName = (string)cmd.ExecuteScalar();
Seite 96 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
10.6 DataReader Objekt
•
Bietet die Möglichkeit, einen Ergebnis-Datenstrom von einer Datenquelle zu erhalten
•
Der Datenstrom kann nur in einer Richtung ausgelesen werden (forward-only, readonly)
•
Typsicherer Zugriff
•
Datenzugriffsmethoden:
- Typ und Spaltenindex: myRow.GetString(0)
- Über Attributnamen: myRow[„Attribut“]
- Read: Bewegt den Reader zum nächsten Datensatz
•
repräsentiert einen unidirektionalen (forward-only), nur-Lesen (read-only) Zugriff auf
die Ergebnismenge in Form eines serverseitigen Cursors. D.h. die Daten werden direkt
vom Server geholt und erfordern deshalb eine stehende Datenbank-Verbindung.
•
Die Methode Read() liefert bei jedem Aufruf den folgenden Datensatz in der
Ergebnismenge, ohne eine Möglichkeit, wieder auf den vorherigen oder den ersten
Datensatz zurückzugehen. Durch diese Einschränkungen wird eine hoch-performante
Implementierung des Cursors möglich. Dadurch soll die Dauer der
Datenbankverbindung auch möglichst kurz gehalten werden.
•
Die Verarbeitung der Daten geschieht offline im Data Set.
Seite 97 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Klassendiagramm (Auszug) des ManagedProviders
Seite 98 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
10.7 DataAdapter
Der DataAdapter „weiß“, wie eine Tabelle aus der Datenbank geladen wird und schreibt
Änderungen zurück.
Der DataAdapter wird explizit für ein bestimmtes Verhalten konfiguriert, das das
Recordset implizit zeigt.
•
Schnittstelle über die in beide Richtungen Informationen ausgetauscht werden können
•
Enthält die Methoden Fill (DataSet) und Update (DataSet) – hierzu wird jeweils ein
DBCommand-Objekt verwendet, eines zum Ermitteln der Daten mittels SELECT und
eines für INSERT, UPDATE und DELETE. Zum Füllen der Datenmenge muss der
Adapter mit einem DataReader arbeiten, dies ist der einzige Weg ist, die Daten von
der Datenquelle abzuholen. Die Updates kann er direkt ausführen, indem er ein
ExecuteNonQuery() auf dem entsprechenden DbCommand-Objekt aufruft
•
Mapping zwischen Tabellen und Spalten
•
Benutzer kann die voreingestellten Kommandos überschreiben (insert / update /
delete), um z.B. StoredProcedures auszuführen
•
Erlaubt es, ein DataSet aus mehreren Datenquellen zu füllen
•
DbDataAdapter ist kein bloßes Interface sondern eine abstrakte Basisklasse innerhalb
des .NET-Frameworks.
•
Die speziellen DbDataAdapter-Nachkommen der einzelnen Managed Provider erben
somit nicht nur die Verpflichtung, bestimmte Methoden anzubieten sondern auch
vorhandene Implementierungen. Eine Schnittstelle IDbDataAdapter gibt es allerdings
auch.
•
Intern verwendet ein DataAdapter ein Command-Objekt für das Abfragekommando
und erzeugt damit einen DataReader. Über ihn werden gegebenenfalls
Metainformationen zum Aufbau der Zieltabellenstruktur ermittelt und schließlich die
Resultatsmenge traversiert, um die Datensätze einzeln in DataRow-Objekte zu
übertragen und an die Tabelle anzuhängen.
Seite 99 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
10.8 DataSet Objekt
•
Nicht mehr Bestandteil des
ManagedProviders, sondern
datenbezogene Komponente
(Datenhaltung)
•
Lokaler Cache für Daten
- ähnlich einer relationalen
Datenbank, die im
Arbeitsspeicher liegt
- Daten sind disconnected
von der Datenquelle
(connect, execute query,
disconnect)
•
Universeller Datencontainer,
unabhängig von einer
speziellen Datenquelle (nicht
nur zur Verwendung mit
Datenbanken, auch XML,
Email, etc.)
Interner Aufbau eines DataSet-Objektes
•
Keinerlei Bezug zur Datenquelle
•
Daten können aus XML geholt und zurückgeschrieben werden – also eine
Serialisierung in XML durch das DataSet mittels der Methoden: WriteXml(),
WriteXmlSchema(), ReadXml()
•
DataSets enthalten Tabellen, Spalten, Relationen, Bedingungen, Zeilen (wie
relationale DB)
•
Die Objektstruktur des DataSets wird von den DbDataAdaptern erzeugt und gefüllt,
kann dann von der Applikation verarbeitet werden und von dem selben DbDataAdapter
wieder mit der eigentlichen Datenquelle synchronisiert.
•
Ein DataSet enthält eine oder mehrere Daten-Tabellen, die wiederum aus Zeilen und
Spalten aufgebaut sind. DataRow-Objekte enthalten die Daten eines einzelnen
Datensatzes, während DataColumn-Objekt die Spalten einer Datenbank-Tabelle
beschreiben (nur MetaDaten, keine Inhalte).
•
Das wesentlich Neue am DataSet im Vergleich zu einem ADO RecordSet ist, dass es
mehr als eine Tabelle aufnehmen kann. Über die DataTableCollection können die
einzelnen Tabellen angesprochen werden, und es ist auch möglich, jederzeit neue
Tabellen hinzuzufügen. Das DataSet kann also auch völlig ohne Managed Provider als
In-Memory-Datenbank betrieben werden.
•
Zwischen den Tabellen im DataSet können auch Beziehungen in Form von Equate
Joins verwaltet werden. Die DataRelationCollection verwaltet beliebig viele
DataRelations, von denen jede eine Beziehung zwischen zwei Tabellen herstellt. Die
Kopplung wird dabei über die Gleichsetzung der Werte zweier Spalten hergestellt, also
nach dem Schema ORDER.Id = ORDERDETAIL.OrderId.
Seite 100 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
Beispiel (Befüllen eines DataSets mittels DataAdapter):
SqlConnection conn = new SqlConnection(“...”);
DataSet ds = new DataSet();
SqlDataAdapter adap = new SqlDataAdapter(“select customerid from customers”,
conn);
adap.Fill(ds, “kunden”);
10.9 DataTable
•
Kann auf physische Tabelle in der Datenquelle gemapped sein
•
Kann durch DataRelations mit anderen DataTables verbunden sein
•
Neue Zeilen können mit der Add()-Methode einfach zur Tabelle hinzugefügt werden,
während vorhandene Zeilen interessanterweise direkt über einen Index angesprochen
werden. Bei einer speicherbasierten Datenbank ist das möglich.
•
Wichtige Properties:
- Columns
- Rows
- ParentRelations
- Constraints
- PrimaryKey
Beispiel:
DataSet ds = new DataSet();
// Neues DataTable Objekt erzeugen
DataTable dt= new DataTable( "Customer" );
// Spalten erzeugen und einfügen
DataColumn dc = new DataColumn( "Cust_id", Int32 );
dt.Columns.Add( dc );
dt.PrimaryKey = dc;
dt.Columns.Add( "Name", String );
dt.Columns.Add( "StreetNo", String );
dt.Columns.Add( "City", String );
// DataTable Objekt in DataSet einfügen
ds.Tables.Add( dt );
Seite 101 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
10.10 DataRelation
•
wird für logische Relationen verwendet
•
erzeugt Relation zwischen zwei DataTable Objekten
•
benötigt ein DataColumn Objekt von jedem DataTable Objekt
•
Der DataType beider DataColumns muss gleich sein (Es kann bspw. keine Int32
DataColumn mit einem String DataColumn verbunden werden)
Beispiel zur Erzeugung einer DataRelation:
•
Suche die zu verbindenden DataColumn Objekte
•
Erzeuge die DataRelation mit den Columns
•
Füge die Relation in das DataSet ein
DataColumn parentCol, childCol;
parentCol = DataSet.Tables["Kunden"].Columns["AboID"];
childCol = DataSet.Tables["Abomodell"].Columns["AboID"];
// Erzeuge die DataRelation mit Namen "Kde_Abo"
DataRelation dr;
dr = New DataRelation("Kde_Abo", parentCol, childCol);
// DataRelation zu DataSet hinzufügen
ds.Relations.Add( dr );
// Kurzversion zur Erzeugung einer DataRelation
ds.Relations.Add("Kde_Abo",
ds.Tables["Kunden"].Columns["AboID"],
ds.Tables["Abomodell"].Columns["AboID"]);
// Navigiere durch die verknüpften Tabellen
foreach (DataRow custRow in ds.Tables["Kunden"].Rows){
Console.WriteLine(custRow["KundeID"]);
foreach (DataRow orderRow in custRow.GetChildRows(Kde_Abo)){
Console.WriteLine(orderRow["AboID"]);
}
}
Seite 102 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
10.11 XML
•
DataSet bietet Methoden mit denen man XML lesen und schreiben kann
- ReadXml: Liest beliebige XML Schema und Daten in ein DataSet
- ReadXmlSchema: Liest XML Schema in ein DataSet
- Zum Schreiben: WriteXml, WriteXmlSchema
•
Schema kann als XSD geladen/gespeichert werden
•
Schema kann von beliebigen XML Daten automatisch erzeugt werden
10.12 Fazit
Ausgehend von den anfänglichen Fragestellungen ist Folgendes zusammenzufassen:
6. Wie verwendet das Anwendungsprogramm die Funktionalität der Datenbank?
Durch Klassen / Schnittstellen aus dem Namensraum System.Data und entsprechende
Methodenaufrufe..
7. Wie findet der Datenaustausch zwischen Anwendung und Datenbank statt?
Durch das DataSet, das vom ManagedProvider erzeugt, und in Form von
Objektinstanzen innerhalb der Anwendung zur Verfügung gestellt werden. Der
Datenaustausch beschränkt sich auf kurze Zugriffe, gearbeitet wird auf Kopien der
Daten im RAM.
8. Wie wird eine Ergebnismenge der Datenbank an die Anwendung übergeben?
Es werden die gesamten Daten auf einmal von der Datenbank in den Speicher der
Applikation übertragen, auf diesem Speicher kann dann gearbeitet werden und alle
Änderungen können auf einen Schlag zurückübertragen werden. (Nicht wie sonst
mittels Cursor)
9. Zu welchem Zeitpunkt findet welche Aktion der Datenbank statt?
Die Reihenfolge der Aktionen, die auf der Datenbank ausgeführt werden erledigt der
ManagedProvider, und ist für die Anwendung nicht relevant. Innerhalb der Anwendung
muss lediglich die richtige Reihenfolge bei der Erzeugung von Objektinstanzen und
dem Aufruf entsprechender Zugriffsmethoden eingehalten werden.
10. Welche Art von Kopplung entsteht zwischen Anwendungsprogramm und
Datenbank?
Die Kopplung findet ausschließlich über die Verwendung der standardisierten
Schnittstelle (Klassen, Methoden) aus dem Namensraum System.Data statt.
Aufgabenteilung zwischen Datenhaltung und Datenzugriff.
Seite 103 von 104
Datenbankprogrammierung
Hochschule Ravensburg-Weingarten
Dipl.-Ing. (FH) D. Schiffer
11 Literatur:
[1] SQL. Der Schlüssel zu relationalen Datenbanken
- Gregor Kuhlmann, Friedrich Müllmerstadt
ISBN: ISBN-10: 3499612453, ISBN-13: 978-3499612459
Preis: 9,90 EUR
[2] Weitere Informationen zu Joins finden Sie unter
- Satya Komatineni
http://www.onjava.com/lpt/a/4443#1 oder
http://www.onjava.com/pub/a/onjava/2004/01/07/SQLJoins.html
[3] Vorlesung Datenbanken
- Mario Jeckle
http://www.jeckle.de/vorlesung/datenbanken/script.html
[4] Vorlesung eBusiness
- Mario Jeckle
http://www.jeckle.de/vorlesung/eBusinessEng/index.html
[5] Vorlesung Datenbank-Anwendungen
- Mario Jeckle
http://www.jeckle.de/vorlesung/DB-Anwendungen/index.html
[6] Vorlesung XML
- Mario Jeckle
http://www.jeckle.de/vorlesung/xml/index.html
[7] Impedance Mismatch
- Alexandra Schäfer
http://www.sigs.de/publications/os/2003/03/schafer_OS_03_03.pdf
[8] Trigger Happy
- Martin Rennhackkamp
http://www.dbmsmag.com/9605d17.html
[9] Stored Procedures for Java Programmers
- Nic Ferrier
http://www.onjava.com/pub/a/onjava/2003/08/13/stored_procedures.html
[10] Struts Best Practices
- Vic Cekvenich, Wolfgang Gehner
ISBN : 3-89864-284-4
Preis : 32,00 EUR
http://www.dpunkt.de/buecher/struts.html
[11] Oracle PL/SQL Tutorial - Die Programmiersprache von Oracle - Ein Nachschlagewerk auf Basis von
Beispielen
- TAKTUM Informatik GmbH
http://www.datenbank-plsql.de/
[12] PL/SQL
http://www.orafaq.com/faq/plsql/
[13] PL/SQL User's Guide and Reference 10g Release 1 (10.1)
Copyright © 1996, 2003 Oracle Corporation
http://www.oracle-10g.de/oracle_10g_documentation/appdev.101/b10807/toc.htm
[14] Design Patterns. Addison-Wesley Professional, 1995,
- Erich Gamma et al.
ISBN : 0201633612
Entwurfsmuster. Addison-Wesley, 2004,
- Erich Gamma et al.
ISBN 3827321999
[15] Weitere Links unter : http://fh.atbits.de/ => weiterführende Links
[16] Struts Best Practices – Die Erstellung komplexer Webanwendungen, 2004
- Vic Cekvenich, Wolfgang Gehner
ISBN: 3898642844
Preis : 32,00 EUR
[17] DAO
http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html
[18] Core J2EE Patterns: Best Practices and Design Strategies 2nd Edition
- Deepak Alur, John Crupi and Dan Malks
Publisher: Prentice Hall / Sun Microsystems Press
ISBN:0131422464; 2nd Edition (June, 2003)
http://www.corej2eepatterns.com/Patterns2ndEd/DataAccessObject.htm
[19] http://www.torsten-horn.de/techdocs/sw-patterns.htm
Seite 104 von 104
Herunterladen