THEMA: JDBC/SQL

Werbung
THEMA: JDBC/SQL
Vortrag: Pascal Gbodogbe am
14.6.2001
TEIL_1: SQL
&
TEIL_2: JDBC
Einführung:
Durch die Einführung der JDBC-Vorlesung können
wir merken:
*JDBC stellt eine standarisierte Methode zum Zugriff
auf
Datenbanken aus Java-Programmen zur
Verfügung.
*JDBC enthält die Möglichkeiten:
– Um Verbindungen zu Datenbanken zu öffnen und
zu schließen,
– Um SQL-Anweisungen an Datenbanken zu
schicken
– und um die Antworten entgegenzunehmen und
deren Auswertung im Java-Programm zu
ermöglichen.
• JDBC baut vollständig auf SQL auf. SQL "Standard
Query Language". Die Kommunikation zwischen
dem Java-Programm und der Datenbank erfolgt über SQLAnweisungen. Diese werden als Text an die
Datenbank geschickt, die daraufhin als Antwort
die Ergebnisse zurückschickt. Die SQLAnweisungen werden nicht vom Java-Compiler auf
Korrektheit geprüft. Fehler können also erst durch
die Datenbank festgestellt werden.
Bevor wir in Detail bei JDBC gehen , wollen wir
SQL in Überblick sehen.
TEIL_1: SQL
• SQL - ein Überblick
• Unser Überblick ist unterteilt in :
- Tabellendeklaration in SQL,
- Anfragen in SQL,
- Änderungsoperationen in SQL, und
- Definition von Sichten in SQL
• Tabellendeklaration in SQL:
Bei der Deklaration einer Tabelle müssen folgenden
Punkte festgelegt werden:
* Attribute als Namen der Spalten,
* Wertebereiche als Angaben von Datentypen
für die
Einträge in Spalten,
* Relationenschemata als Typdefinitionen von
Tabellen,
* Schlüssel zur Festlegung eindeutiger
Identifikatoren
für Tupel in Tabellen, und
* die Deklaration von Fremdschlüssel zur
Erzwingung
der referentiellen Integrität bei
Verweisen auf Einträge in anderen Tabellen.
• Die Sprache zur Festlegung von
Datenbankbeschreibungen nennt man database
definition language, kurz DDL. Die
Tabellendeklaration in SQL sieht der Definition
von Record-Strukturen
in
Programmiersprachen
nicht unähnlich, erweitert
diese aber um die RDBMS-spezifischen
Aspecte. Folgende Beispieldeklaration zeigt den
prinzipiellen Aufbau:
CREATE TABLE book (
isbn VARCHAR ( 30 ) NOT NULL,
title VARCHAR ( 50 ) ,
price FLOAT );
• Wertebereiche für Attribute: Die Möglichen
Wertebereiche für Attribute sind die Datentypen:
* smallint (oder auch integer2),
* float (p) (oder auch kurz float),
* decimal ( p , q ) und numeric ( p , q ) mit
jeweils q Nachkommastellen,
* character (n) (oder kurz char(n), bei n = 1 auch
char ) für Strings fester Länge n,
* character varying (n)(oder kurz varchar(n) ) für
Strings variabler Länge bis zur Maximallänge n,
* bit (n) oder bit varying (n) analog für Bitfolgen und
* date, time bzw. timestamp für Datums-, Zeitund kombinierte Datums-Zeit-Angaben.
• BLOB und CLOB: Eine weitere Neuerung im
Standard sind die sogenannten large objectDatentypen BLOB und CLOB. BLOB steht für
binary large object; CLOB für
charater large object. Die Werte entsprechender
Datentypen sind ( in der Regel sehr lange ) Folgen von
Binärwerten ( bei einem BLOB ) oder von CharacterZeichen (CLOB), wie sie zum Beispiel bei der
Abspeicherung eines digitalisierten Photos oder
eines unstrukturierten Textes entstehen.
• Nullwerte: Mit der Klausel NOT NULL
können in bestimmten Spalten Nullwerte als
Attributwerte ausgeschlossen werden.
• Primärschlüssel: Das folgende Beispiel
zeigt die Definition von identifizierenden
Schlüsseln ( in SQL als Primärschlüssel
bezeichnet: derjenige von mehreren möglichen
CREATE TABLE book (
isbn VARCHAR ( 30 ) NOT NULL,
title VARCHAR ( 50 ) ,
price FLOAT
publ_id INTEGER ,
PRIMARY KEY ( isbn ) ,
FOREIGN KEY publ_id REFERENCES
(publisher) );
• Anfragen in SQL: Eine erste SQL-Anfrage an
eine einzelne Tabelle könnte wie folgt lauten:
SELECT title , price
FROM book
WHERE isbn = ‘ 3 - 929821 - 31 - 1 ‘ ;
Diese Anfrage gibt die Relation an, aus der
Daten gewonnen werden sollen ( hinter FROM ),
Selektionsbedingung und die Attribute einer
Projektion. Die Angaben der
Selektionsbedingung können wie gewohnt durch
boolesche Operatoren verbunden werden, die als
englische Worte notiert werden (AND, OR und
NOT). Als Basisprädikate sind insbesondere die
bekannten Vergleichsprädikate erlaubt.
• Duplikatunterdrückung:
SQL hat eine Multimengensemantik; die
Duplikatunterdrückung muß also explizit erzwungen werden,
wenn sie erwünscht ist:
SELECT
DISTINCT lastname
FROM author;
• Kreuzprodukt: Als wesentliche Möglichkeit,
Daten aus zwei Relationen miteinander zu
folgende Anfrage berechnet also das Kreuzprodukt
zweier Relationen, wobei die Angabe * die
Ausgabe aller Attribute erwirkt:
SELECT *
FROM book, publisher;
• Verbundbedingungen in SQL:
Der ( eigentlich erwünschte) Verbund muß durch
die Angabe einer Verbundbedingung erzwungen
werden. Hierzu müssen gleichlautende Attribute
des Kreuzproduktes unterschieden werden - einen expliziten
Umbenennungsoperator für Zwischenergebnisse wie
in der Relationenalgebra kennt SQL nicht. Dies
erfolgt über sogenannte Tupelvariablen, die im
Standardfall aus den Namen der Relationen
SELECT *
FROM book , publisher
WHERE book . Publ_id = publisher . Publ_id;
• Selbstverbund: Natürlich können auch neue
Variablennamen generiert werden - bei Selbstverbunden
ist dies sogar notwendig, wie folgende Anfrage
zeigt, die die ISBN von Büchern berechnet, die von
zwei Autoren gemeinsam geschrieben wurde:
SELECT ba1 . isbn
FROM book_author ba1 , book_author ba2
WHERE ba1 . isbn = ba2 . isbn AND
ba1 . a_id = 3 AND
ba2 . a_id = 4;
In der letzten Standardisierungsversion SQL-92
auch explizite Verbundoperatoren eingeführt. So
berechnet die folgende Anfrage tatsächlich den
natürlichen Verbund:
SELECT *
FROM book NATURAL JOIN publisher;
Üblich ist aber in SQL-Anwendungen noch die
vorher beschriebene Variante, die dem ersten
SQL-Standard entspricht. Die erste Variante wird
auch von allen RDBMS unterstützt, was beim SQL92-Standard nicht automatisch im vollem Umfang
der Fall ist.
• Vereinigung und Differenz in SQL: Der
Algebraoperator Vereinigung wird in SQL explizit mit
UNION notiert. Der Differenzbildung hingegen wird
in der Regel durch geschachtelte Anfragen
realisiert, wie folgendes Beispiel zeigt:
SELECT *
FROM customer
WHERE customer . cust_id NOT IN (
SELECT cust_id
FROM book_order );
Da geschachtelte Unteranfragen etwas komplexer
in der Semantik und den Syntaktischen Varianten
sind, belassen wir es an dieser Stelle mit diesem
einfachen Beispiel.
• Aggregierung:
* Die Aggregatfunktionen COUNT, SUM, AVG,
MAX und MIN erlauben es, aggregierte Werte für
Spalten zu bilden.
• Gruppierung:
* Die Gruppierung mittels GROUP BY und
gruppiert Tupel in einer Relation und erlaubt die
Bildung eines aggregierenden Resultats für jede
Gruppe.
• Sortierung: Die Angabe von ORDER BY
sortiert ein Anfrageergebnis.
• Änderungsoperationen in SQL
In SQL werden die folgenden
Änderungsoperationen unterstützt:
* Die INSERT-Anweisung ermöglicht das Einfügen
eines oder mehrerer Tupel in eine Basisrelation
oder Sicht.
* Mittels UPDATE können ein oder mehrere Tupel in
eine Basisrelation oder Sicht geändert werden.
* Die DELETE-Anweisung realisiert das Löschen
eines oder mehrerer Tupel aus einer Basisrelation
oder Sicht.
• Die INSERT-Anweisung: Für das Einfügen
mittels
INSERT kann man zwei Varianten unterscheiden.
In der ersten Variante werden konkrete Werte in
eine Tabelle eingetragen:
INSERT
INTO publisher (publ_id , name)
VALUES (1 , ‘ dpunkt - Verlag ‘ ) ;
Die Angabe der Attribute hinter dem
Tabellenname ist notwendig, wenn nicht alle
Attribute gesetzt werden sollen und die restlichen mit
Nullwerten gefüllt werden sollen.
In der zweiten Variante werden die einzufügenden
Tupel mittels einer Anfrage berechnet:
INSERT INTO publisher (publ_id , name)
• Die UPDATE-Anweisung: Die
Attributwertänderung mittels UPDATE entspricht
dem Zuweisungsoperator aus
Programmiersprachen ( so kann der alte
Wert zur Berechnung des neuen genutzt werden).
UPDATE book
SET price = price * 0.9
WHERE stock < 10;
Mehrere Attribute einer Tabelle können gleichzeitig
gesetzt werden.
• Die DELETE-Anweisung: Bei der Löschoperation
kann neben der Angabe der Tabelle eine
qualifizierende Bedingung angegeben werden, die die zu löschenden
Tupeln festlegt:
DELETE FROM book_order
• Definition von Sichten in SQL:
• Sichtdefinition in SQL: In SQL wird die
Sichtdefinition durch die Schlüsselwörter CREATE
VIEW, gefolgt vom Namen der Sicht, eingeleitet.
Nach dem Sichtnamen steht im einfachsten Fall
das Schlüsselwort AS und eine SQL-Anfrage, die
die Sicht definiert (optionale Teile sind in eckige
Klammern gesetzt):
CREATE VIEW SichtName
[ SchemaDeklaration ]
AS SQLAnfrage
[ WITH CHECK OPTION ]
Das Relationenschema der Sicht, also Name und
Datentyp der einzelnen Spalten, wird aus der
Anfrage abgeleitet und muß nicht explizit
angegeben werden. Allerdings kann optional eine
eingefügt werden, in der die Namen der Spalten
festgelegt werden können. Dieses Sprachkonstrukt
erlaubt auch die Umbenennung von Spalten.
• WITH CHECK OPTION: Ein weiterer optionaler
Teil
einer Sichtdeklaration ist die Angabe der
Schlüsselwörter WITH CHECK OPTION am Ende
der Sichtdefinition. Diese Angabe legt fest, ob
Änderungen der Sicht, die den in ihr nicht
sichtbaren Teil der Datenbank beeinflussen, in
einem Test erkannt und abgewiesen werden
sollen.
Das folgende Beispiel zeigt eine Sichtdeklaration in
SQL:
CREATE VIEW book_info AS
SELECT b . isbn, b . title, b . price ,
WHERE
b . isbn = ba . isbn AND
ba .a_id = a . a_id AND
b . publ_id = p . publ_id;
• Änderungsoperationen auf Sichten: Soll eine
Änderung auf einer Sicht in Änderungen auf der
Basisdatenbank umgesetzt werden, müssen eine
Reihe von notwendigen oder wünschenswerten
Kriterien eingehalten werden:
* Effektkonformität: Der Benutzer, der die
Änderung auf der Sicht formuliert, soll nach der
ausgeführten Änderung auf der Basisdatenbank
im nächsten Zustand eine Ausprägung der
Sicht erhalten, die dem Effekt entspricht, als wäre
die Änderung auf der Sichtrelation direkt ausgeführt worden. Dieses Kriterium fordert in gewissem
Sinne die Korrektheit der Transformation.
* Minimalität bei Sichtäderungen: Die
Basisdatenbank sollte nur minimal geändert
werden, um den erwähnten Effekt zu erhalten.
* Konsistenzerhaltung bei Sichtänderungen:
Die Änderung einer Sicht darf zu keinen
Integritätsverletzungen der Basisdatenbank führen.
* Zugriffskontrolle bei Sichtänderungen: Wird die
Sicht aus Gründen der Zugriffskontrolle
eingeführt, darf der bewußt ausgeblendete Teil
der Basisdatenbank von Änderungen der Sicht
nicht betroffen werden.
* Zugriffsrechte: In SQL-Datenbanken wird eine
einfache Modellierung von Zugriffsrechten auf
Datenbestände unterstützt. Dabei haben
Zugriffsrechte den folgenden Aufbau (Subjekt,
Die einzelnen Punkte haben die folgende Bedeutung:
* Autorisierungsidentifikator: (kurz AutorisierungsID)
ist eine interne Kennung
eines>>Datenbankbenutzers<<. Eine Typische
AutorisierungsID kann die Benutzerkennung des
Betriebssystems sein. Andere Beispiele wären
Kennungen von Zugreifenden Softwaresystemen
oder auch Kennungen von Benutzergruppen.
* Datenbank-Ausschnitte sind im
Relationenmodell
gespeicherte Relationen und Sichten, aber auch
ganze Datenbanken oder Schemainformationen.
* Unter den Operationen sind insbesondere die
klassischen Operationen des Lesens, Einfügens,
Änderns und Löschens von Datensätzen zu
verstehen. Je nach konkret realisiertem System
* Rechtevergabe mittelsGRANT: Im SQL-Standard
werden Rechte mittels der GRANT-Anweisung
vergeben, die nach folgendem Muster notiert wird:
GRANT Rechte
ON Tabelle
TO BenutzerListe
[WITH GRANT OPTION]
Ein Spezialfall der Angabe eines AutorisierungsIDs
ist die Angabe PUBLIC.
* Trigger: Ein Trigger besteht im wesentlichen
aus der Angabe eines Auslösers und der Angabe von
auszuführenden Folgeactionen:
CREATE TRIGGER . . .
ON Opreation : ( Anweisungen );
Als Beispiel für den Einsatz von Triggern
betrachten wir die Realisierung eines berechneten
Attributs durch Trigger. Das Beispiel basiert auf
zwei Relationen, der Relation customer mit einem
zusätzlichen Attribut num_orders für die Anzahl
der Aufträge und einer zweiten Relation
book_order. Der Wert des Attributs num_orders soll
vom System verwaltet werden. Hierzu definieren
wir einen Trigger wie folgt:
CREATE TRIGGER OrderCounter
ON INSERTION OF book_order o:
UPDATE customer
SET num_orders = num_order + 1
WHERE cust_id = NEW o . Cust_id;
Neben dem vorgestellten syntaktischen
Grundgerüst der Definition von Triggern sind
die insbesondere den Zeitpunkt der
Triggeraktivierung betreffen:
* Zeitpunkt der Aktivierung von Trigger:
Mittels IMMEDIATE bzw. DEFERRED kann der
Zeitpunkt der Aktivierung desTriggers festgelegt
werden: sofort nach
der aktivierenden Operation oder am Ende der
Transaktion. Aktuelle Implementierungen
unterstützen oft nur den IMMEDIATE-Modus.
Die Angabe FOR EACH ROW aktiviert den
Trigger für alle Einzeländerungen einer
mengenwertigen Änderung separat.
Die Angaben BEFORE und AFTER steurn etwa
in der aktuellen Version des kommerziellen
Systems Oracle, ob der Trigger direkt vor oder nach
einer Änderung aktiviert wird.
Mit REFERENCING NEW AS bzw.
REFERENCING OLD AS kann eine Tupelvariable
an die neu eingefügten bzw. gerade gelöschten
(>>alten<<) Tupel einer Relation gebunden
werden. Diese >>neuen<< bzw. >>alten<< Tupel
werden auch als Elemente der sogenannten
Differenzrelation bezeichnet.
TEIL_2: JDBC
• JDBC: Java DataBase Connectivity
• Das JDBC-Package stellt eine vollständige
Schnittstelle zu SQL-Datenbanken zur Verfügung und
erlaubt damit den Einsatz von Java-Programmen
und Java-Applets (Applet ist eine Bezeichnung für
ein Java-Progamm, das in eine HTML-Seite
integriert ist.) als Front-Ends für kommerzielle
Applikationen.
• JDBC erfordert die direkte Nutzung von SQLAnweisun-gen (Low-Level-API (Application Programmer
Interface) )
• JDBC besteht einer Menge von Klassen und
Schnittstellen, die im Java-Package java . sql
zusammengefaßt sind. Die wichtigsten Klassen und
Schnittstellen sind hierbei:
• java . sql . DriverManager: bildet den
Einstiegspunkt, indem über diese Klasse Treiber registriert und
Verbindungen zur Datenbank aufgebaut werden können.
• Java . sql . Connection: repräsentiert eine
Datenbankverbindung.
• Java . sql . Statement: ermöglicht die Ausführung
von SQL-Anwendungen über eine gegebene
Verbindung.
• Java . sql .ResultSet: verwaltet die Ergebnisse einer
JDBC am Beispiel
• Ablauf einer JDBC-Anwendung
• Um die verschiedenen Aspekte der JDBCProgrammierung einzugehen, wollen wir uns mit einigen kurze
Beispiele befassen. Diese Programmausschnitte sollen
die An-wendung der wichtigsten Klassen
demonstrieren und gleichzeitig illustrieren, wie die
Benutzung von JDBC ist.
Der prinzipielle Ablauf
einer JDBC-Datenbankanwen-dung umfaßt die
folgenden Schritte:
1. Aufbau einer Verbindung zur Datenbank
2. Senden einer SQL-Anweisung
• Explizites Laden
Voraussetzung für den Aufbau einer
Datenbankverbindung
ist das Laden eines geeigneten Treibers. Hierzu
werden in
JDBC zwei Varianten unterstützt. Zum einen kann
der
Treiber (d.h. die Java-Klasse des Treibers) explizit
im Programm geladen werden:
Class . forName( “ oracle . jdbc . driver .OracleDriver
“);
• Automatisches Laden
Bei der zweiten Variante wird eine Liste von
Treibern, je-
• Verbindungsaufbau
Der nächste Schritt ist der Verbindungsaufbau.
Hierfür stellt der Treibermanager, d. h. die Klasse
Java . sql . DriverManager, eine eigene Methode getConnection
bereit.
Als Argument dieser Methode muß eine URL
(Uniform
Resource Locator)angegeben werden, die den
Verbindungs
mechanismus und damit den zu verwendenden
Treiber be-zeichnet. Zusätzlich sind noch
Benutzername und Passwort zu übergeben. Der Aufruf der Methode
liefert im Erfolgsfall ein Connection-Objekt.
Connection con;
String url =
Mit dem Connection-Objekt kann nun eine SQLAnweisung erzeugt werden. Wir wollen zunächst eine
einfache
Anfrage betrachten, die mit executeQuery
ausgeführt wird.
String query = “ SELECT title, price, “ + “
stock
FROM book “;
Statement stmt = con.createStatement ( ) ;
ResultSet rs = stmt .executeQuery ( query);
• Ausführung von Anfragen
Die executeQuery-Methode liefert ein ResultSetObjekt, das
die Ergebnisse der Anfrage verwaltet. Die Navigation
über
griffen werden kann. In JDBC existiert jedoch kein
expli-ziter Cursor, vielmehr wird die aktuelle Position
in der Ergebnismenge vom ResultSet intern verwaltet. Zum
Weitersetzen des Cursors wird die Methode next
verwendet. Diese Methode liefert so lange den Wert
true, bis das Tabellenende erreicht ist. Außerdem ist zu beachten,
daß der ResultSet -Cursor zu Beginn vor dem
ersten Tupel positioniert ist, d.h., bevor ein Tupel gelesen werden
kann, muß die Methode next aufgerufen werden.
• Zugriff auf Spalten
Nachdem der Cursor positioniert ist, können die
Spalten-
nen Datentypen und die korrespondierenden
Java-Typen zur Verfügung. So heißt die Methode
zum Lesen einer Zeichenkette beispielsweise getString, wobei der
Attributtyp
der Relation hier VARCHAR und der Java-Typ des
Ergebnisses java .Lang . String ist. Der Zugriff auf eine
konkrete Spalte der Ergebnisrelation mit Hilfe der getXXXMethoden erfolgt entweder über den Index der Spalte
oder den Spaltennamen. Wichtig ist dabei, daß der
Spaltenindex mit 1 beginnt, d. h., die erste Spalte
wird durch 1 bezeichnet, die zweite mit 2 usw.
Insgesamt kann das Ergebnis der Anfrage wie folgt
double d = rs .getDouble (2);
int
i = rs .getInt (3);
System . out .println(s + “ “ + d + “ “ + i
);
}
• Ressourcenfreigabe
Zum Abschluß werden die benutzten Ressourcen
durch Aufruf der close-Methoden von ResultSet
und Statement freigegeben:
rs .close ( );
stmt .close ( );
• Fehlerbehandlung
Fehler werden in JDBC grundsätzlich als
Ausnahmen (Exception ) der Klasse SQLException
signalisiert und sind daher in geeigneter Weise mit
• zufangen und zu behandeln.Details zu einem
aufgetretenen
Fehler können über die Methode getMessage
ermittelt wer-den, die eine Zeichenkette mit der
Beschreibung des Fehlers liefert. Der
entsprechende Programmausschnitt zur
Fehlerbehandlung ist danach wie folgt zu formulieren:
try {
/ / Aufruf von JDBC-Anweisungen,
/ / die Exception generieren
} catch ( SQLException exc) {
System . out . println ( “ SQLException:
“+
exc . getMessage ( ) );
}
können wir nun die erste vollständige JDBCAnwendung erstellen.
Beispiel 1. JDBC-Programm
import java . sql .*;
public class JdbcDemo1 {
public static void main (String [ ] args) {
String driverClass =
“ oracle . jdbc . driver . OracleDriver “;
try { Class . forName ( driverClass );
} catch ( ClassNotFoundException exc ) {
System . out . Println ( exc . getMessage (
) );
System .exit ( 1 );
}
try {
String url =
“jdbc : oracle :
thin:@antarctica:1521:mydb“;
String query = “ SELECT title, price, “
+
“ stock FROM book “;
Connection con =
DriverManager . getConnection ( url , “
tux“ ,
“ pingus “ );
Statement stmt = con .createStatement
( );
ResultSet rs = stmt . executeQuery
(query);
int i = rs . getInt (3);
System . out . println ( s + “ , “ + d
+
“ DM, “ + i );
}
} catch ( SQLException exc ) {
System . out . println (“
SQLException: “
+ exc . getMessage ( ) );
}
}
}
Nachdem wir einen ersten Überblick zur
Anwendungsentwicklung mit JDBC gewonnen haben, werden wir
JDBC im Detail
• Verbindungsinformationen
Datenbank: mydb; Server: antarctica ;
Benutzer: tux ;
Paßwort: pingus.
• Treiber und Datenbankverbindung
– Treibermanager
Der Treibermanager implementiert in der Klasse java
.sql .DriverManager und bildet die
Vermittlungsschicht
zwischen Anwendung und Datenbanktreiber. Da es
in
jeder Java-Anwendung nur einen Treibermanager
sich grob in drei Gruppen einteilen:
– Registrieren und Laden von Treibern,
– Herstellen einer Verbindung zur Datenbank,
– Konfigurieren der Verbindung (Protokollierung,
Login-
Timeout).
• Laden von Treibern
-Explizit durch Laden der Treiber
Hierbei wird der JDBC-Treiber über den Aufruf
von Class . forName direkt geladen. Einzige
Voraussetzung für diese Methode ist ein
Java-Klassenpfad, der die
Klassen des benötigten JDBC-Treibers enthält. Da
keine weiteren Konfigurationen notwendig sind, ist
dies wohl die Methode, die am häufigsten eingesetzt
-Über die Systemeigenschaft sql . Driver
Die Systemeigenschaft ( Property) umfaßt eine
Liste von Treibern, die durch Doppelpunkt getrennt
sind. Bei der Initialisierung des Treibermanagers, d. h. beim ersten
Aufruf
einer Klassenmethode, werden alle dort
angegebenen Trei-ber geladen. Die Eigenschaft
kann z. B. beim Aufruf des Java-Interpeters
angegeben werden:
java -Djsql . drivers = foo . bar . Driver JdbcDemo1
Eine weitere Möglichkeit sind Property-Files, wie
z. B. durch den Browser HOTJAVA oder den
Applet-Viewer ausgewertet werden.
In beiden Fällen ist der Name der Klasse der
Dokumenta-
Klassennamen einiger JDBC - Treiber
DBMS
Oracle 8
DB2
MiniSQL
MySQL
ODBC
Treiberklasse
oracle . jdbc . driver . OracleDriver
COM . ibm . db2 . jdbc . app .DB2Driver
com . Imaginary . sql . msql . MsqlDriver
org .gjt . mm . mysql . Driver
sun . jdbc . odbc . JdbcOdbcDriver
• Treiber
Registrierung von Tribern
Wird ein JDBC-Treiber geladen, so muß dieser sich
beim Treibermanager registrieren. Hierzu existieren
die Methode
/ / java . sql . DriverManager
static void registerDriver (Driver driver)
throws SQLException;
Die Methode wird normalerweise in einem
Initialisierungs-block ( ein static {…. }-Block) der
Treiberklasse aufgerufen, der beim Laden der Klasse über Class .
forName aus-geführt wird und die
Treiberregistrierung vornimmt.
Entwickler für Datenbanktreibern interessant.
Weiterhin kann man explizit den Treiber für eine
gegebenen JDBC-URL anfordern oder mittels eines
Iterators über alle geladenen Treiber navigieren:
/ / java . sql . DriverManager
static Driver getDriver (String url)
throws SQLException;
static java . Util . Enumeration getDrivers ( )
* Iteraor (Enumeration)
Ein für Container-Klassen ( wie Listen, Bäume,
Felder, usw. ) sehr nützliches Interface, das
eine Möglichkeit bereitstellt, alle Elemente des
Containers nacheinander zu
referenzieren.
Das folgende Beispiel zeigt die Typische Anwendung
einerEnumeration e:
for (Enumeration e = v . elements ( ) ;
e . hasMoreElements ( ) ; ) {
System . out .println (e . nextElement ( ) ) ;
}
Aufbau einer Datenbankverbindung
Verbindungsaufbau
Nach dem Laden eines Treibers kann die
Verbindung zur
Datenbank aufgebaut werden. Der
Treibermanager stellt
hierfür drei verschiedene Methoden bereit, die sich
nur in
der Form der Parameterlisten unterscheiden:
/ / java . sql . DriverManager
static Connection getConnection ( String url )
throws SQLException;
static Connection getConnection ( String url ,java .
util . Properties info ) throws SQLException;
static Connection getConnection (String url ,String
user , String password) throws SQLException;
JDBC-URL
Alle Methoden erwarten einen Uniform
Ressource Locator (URL) als Parameter und liefern
ein Connection-Objekt. Die JDBC-URLs sind im
Aufbau an die aus dem Web bekannten URLs
angelehnt. Sie haben grundsätzlich folgende Form:
jdbc : < subprotocol > : < subname >
Hierbei bezeichnen < subprotocol > den
Mechanismus zur Datenbankverbindung (z. B. ein
konkretes Protokoll) und < subname > ein
Identifikator für die Datenbank. Der <subname >Teil ist außerdem abhängig vom Protokoll und
kann auch eine Netzwerkadresse beinhalten.
Dagegen müssen für eine Verbindung zu einer
Oracle-Datenbank die Netzwerkadresse (Hostname
und Port) des Datenbankservers bzw. Des ListenerProzesses sowie der Name der Datenbank
angegeben werden :
jdbc : oracle : thin : @antarctica : 1521 : mydb
Beim Aufruf der Methode getConnection versucht der
Treibermanager einen Treiber zu finden, der die
angegebene
von java . sql .Driver aufgerufen. Mit dem ersten
gefunde-nen Treiber wird die Verbindung zur
Datenbank hergestellt. Als weitere Parameter für den
Verbindungsaufbau sind insbesondere
Benutzername und Paßwort anzugeben, entweder
direkt oder als Schlüssel-Wert-Paare in Form eines
Property-Objektes.
Timeouts und Protokollierung
Timeout : Mit den weiteren Methoden der Klasse
Driver-Manager kann die maximale Wartezeit für
das Anmelden bei der Datenbank (Timeout)
abgefragt bzw. geändert werden. Die Zeitangabe erfolgt dabei jeweils in
Sekunden:
/ / java . sql . DriverManager
Tracing : Schließlich kann noch ein Ausgabestrom
für die Protokollierung (Tracing) vom
Treibermanager sowie den Treibern angegeben
werden:
/ / java . sql . DriverManager
static java . io . PrintStream getLogStream ( )
static void setLogStream ( java . io . PrintStream
out )
Als Parameter muß hier ein Stream-Objekt
eingesetzt wer-den, wie z. B. System . out oder ein
Strom auf einer geöffneten Datei. Auf diese Weise lassen sich
Fehlermeldungen protokollieren. Durch Übergabe
von null als Parameter kann die Protokollierung
wieder abgeschaltet werden. Diese Methoden sind
in JDBC2 durch setLogWriter bzw.
Connection
Ein Connection-Objekt repräsentiert eine
Verbindung zur Datenbank. Eine Applikation kann
dabei mehrere Verbin-dungen zu einer oder zu
verschiedenen Datenbanken öffnen. Wie bereits
beschrieben, wird eine Verbindung durch Aufruf
der getConnection-Methode des Treibermanagers erzeugt. Über eine geöffnete Verbindung
können
SQL-Anweisungen zur Datenbank gesendet und
Transaktionsabläufe gesteuert werden.Weiterhin lassen sich
Infor-mationen über die Datenbank abfragen.
Katalogzugriff: Der Bereich der Datenbank, auf
den über die Verbindung zugegriffen werden soll,
kann mit den Methoden getCatalog und
/ / java . Sql . Connection
void setCatalog (String Catalog) throws
SQLException;
String getCatalog ( ) throws SQLException;
Jedes Datenbankobjekt (Relationen, Sichten,
Prozeduren,
usw.) ist eindeutig über das Tripel
(Katalogname, Schemaname, Objektname) identifizierbar, wobei im
Normalfall der Katalogname dem Datenbanknamen
und der Schema-name dem Benutzernamen
entspricht. Für eine Verbindung ist der Katalog auf
den Default-Katalog des angemeldeten Benutzers
initialisiert, so daß diese Methoden nur selten
benötigt werden. Die exakte Bedeutung des
Katalognamens ist jedoch vom DBMS abhängig.
sollen.
/ / java . sql . Connection
void setReadOnly ( boolean readOnly )
throws SQLException;
boolean isReadOnly ( ) throws SQLException;
Allerdings bedeutet das nicht, daß
Schreiboperationen damit verboten sind. Vielmehr
ist es ein Hinweis für den Treiber und das DBMS
und z. B. Optimierungen vorzuneh
men. Der aktuelle Modus der Verbindung kann
über isReadOnly abgefragt werden. Für einen
Treiber, der keine Schreibinformationen unterstützt,
wird hier entsprechend auch true zurückgegeben.
Verbindungsabbau: Eine aktive Verbindung zur
Datenbank wird mittels der close-Methode beendet.
/ / java . sql . Connection
void close ( ) throws SQLException;
boolean isClosed ( ) throws SQLException;
Obwohl das Schließen auch automatisch bei der
Freigabe des Objektes durch den Garbage
Collector erfolgt, ist das explizite Schließen die
empfohlene Variante. So können Ressourcen des
DBMS sofort freigegeben werden, wenn sie nicht
mehr benötigt werden. Das automatische Schließen wird dagegen unter Umständen (z. B.
Hauptspeicher) erst viel später erfolgen. Der
Zustand einer Verbindung ist wiederrum über die
Transaktionssteuerung
Der Begriff der Transaktion ist als elementare
Ausführungseinheit zur Überführung der Datenbank von einem
konsistenten Zustand in einen veränderten, konsistenten
Zustand
unter Einhaltung des ACID-Prinzips vorgestellt.
Unter
ACID versteht man:
Atomarität = Ununterbrechbarkeit.
Konsistenz = Integritätserhaltung.
Isolation = Isolation.
Dauerhaftigkeit = Persistenz der Ergebnisse.
Die Kommandos zur Steuerung des
/ / java . Sql . Connection
void commit ( ) throws SQLException;
void rollback ( ) throws SQLException;
Mit der Methode commit wird angezeigt, daß die
Transaktion erfolgreich abgeschlossen werden soll und
alle Än
derungen permanen t in die Datenbank zu
schreiben sind.
Mit rollback wird die aktive Transaktion abgebrochen
alle Änderungen, die im Rahmen dieser Transaktion
durch-geführt wurden, werden rückgängig
gemacht.Ein explizites >> Begin of Transaction <<
existiert dagegen nicht. Tran-saktionen werden - wie
Auto-Commit: Wird eine neue Verbindung zur
Datenbank hergestellt, so befindet sich diese im
Auto-Commit-Modus, d. h., nach jeder Anweisung
wird automatisch ein Commit ausgeführt und eine
Transaktion umfaßt jeweils nur eine Anweisung.
Dieser Modus kann mit der Methode
setAutoCommit ein- (mit true als Parameter) bzw.
ausgeschaltet (false) werden:
/ / java . Sql . Connection
void setAutoCommit (boolean enable)
throws SQLException;
boolean getAutoCommit ( ) throws
SQLException;
Ohne Auto-Commit wird die Transaktion erst mit
das Aabschalten des Auto-Commit-Modus lassen
sich meh
-rere Anweisungen zu einer Transaktion
zusammenfassen. Außerdem können so Änderungen
bis zum Ende der Transaktion rückgängig gemacht werden. Das folgende
Beispiel zeigt die Nutzung von Transaktionen beim
Einfügen mehrerer Datensätze, wobei beim Auftreten eines Fehlers
alle Änderungen zurückgenommen werden.
Die Fehlermeldung wird außerdem durch
Weiterleiten der Ausnahme an den Aufrufer
propagiert:
try {
stmt = con . createStatement ( );
“ 2, 1, 49.90, 20 ) “ );
stmt . executeUpdate ( “ INSERT INTO author
“+
“ VALUES (1, ‘Tad‘ , ‘Williams‘ )“ );
stmt . executeUpdate ( “ INSERT INTO
book_author “ + “ VALUES (‘3- 60893421-9‘ , 1)“);
con . Commit ( );
} catch (SQLException exc) { con . Rollback (
);
throws exc;
}
Für eine Reihe von Anwendungen ist die strenge
Einhaltung
der ACID-Eigenschaften jedoch zu restriktiv und zu
Lauf-
• Ausführung von SQL-Anfrage
In JDBC wird jede SQL-Anweisung durch ein
Statement-Objekt gekapselt. Dieses Objekt sendet
die Anweisung zur Datenbank, liefert das Ergebnis
zurück und verarbeitet
Parameter, die der Anweisung übergeben werden.
Es werden drei Formen von Statements unterstützt:
Arten von Statements:
java . sql .Statement als Basisschnittstelle für alle
anderen Formen erlaubt die Verarbeitung einfacher
Anweisungen ohne Parameter.
Java . sql .PreparedStatement kapselt eine
vorkompilierte Anweisung und wird insbesondere
dann eingesetzt, wenn eine Anweisung mehrfach
und mit verschiedenen IN-Para-
Java . sql .CallableStatement ermöglicht den
Aufruf von gespeicherten Prozeduren mit IN- und
OUT-Parametern.
Erzeugen von Anweisungen
- Erzeugen von Statements
Ein Statement-Objekt wird grundsätzlich über die
Connection-Schnittstelle erzeugt. Hierzu ist für jede
Anweisungsform eine eigene Methode definiert:
/ / java . sql . Connection
Statement createStatement ( ) throws
SQLException;
PreparedStatement preparedStatement (String
sql)
Ausführung von Anweisungen
Die Statement- Schnittstelle definiert als
Basisschnittstelle die Methoden für alle
Anweisungsformen. Zur Ausführung der
Anweisungen stehen drei execute-Methoden zur
Verfügung.
/ / java . sql . Statement
ResultSet executeQuery ( String sql)
throws SQLException;
int executeUpdate (String sql) throws
SQLException;
boolean execute (String sql) throws
SQLException;
-Ausführung von SELECT-Anweisungen
Statement stmt = con . createStatement ( );
ResultSet rs = stmt . executeQuery (“ SELECT *
FROM book“);
Ausführung von DDL- und DML-Anweisungen
DDL-(Database Definition Language) und DML(Database Manager Language) Anweisungen, d. h.
CREATE TABLE oder INSERT, UPDATE und
DELETE, werden über die
Methode executeUpdate zur Datenbank gesendet.
DML-Anweisungen manipulieren Tupel einer
Relation, das Ergebnis ist demnach die Anzahl der betroffenen
Tupel. Für DDL-Anweisungen ist dagegen der
Rückgabewert immer 0.
Int nrows = stmt . executeUpdate (“ DELETE
FROM customer WHERE cust_id = 256 “);
Anweisungen mit mehreren Ergebnisse
Eine besondere Behandlung erfordert
Anweisungen, die
mehrere Ergebnisse liefern. Dies kann z.B. bei
gespeicherten Prozeduren auftreten, die mehrere UPDATEOperationen oder mehrere Anfragen ausführen. Für diese
(eher selte
nen) Fälle wird die Methode execute verwendet.
Diese Methode liefert den Wert true, wenn das Ergebnis der
Anfrage
ein ResultSet-Objekt ist ( z. B. nach einem SELECT
) und false, wenn es ein Integer-Wert ist (z. B. die
Anzahl der
int getUpdateCount ( ) throws SQLException;
boolean getMoreResults ( ) throws SQLException;
Ein ResultSet-Objekt wird danach durch getResultSet
gelie-fert, die Anzahl der betroffenen Tupel mit
getUpdateCount Ob weitere vorliegen und von
welcher Art diese sind, kann mit getMoreResults
erfragt werden. Diese Methode liefert true, wenn
das nächste Ergebnis ein ResultSet-Objekt ist, und
false für einen Integer-Wert. Das jeweils nächste
Ergebnis wird durch getResultSet bzw.
geUpdateCount ermittelt, wobei getResultSet den
Wert null zurückgibt, wenn das Ergebnis ein
Integer-Wert ist und getUpdate-Count einen der
folgenden Werte liefert:
>0 für eine UPDATE-, INSERT- oder DELETEOperation
-1, wenn das Ergebnis ein ResultSet-Objekt ist
oder keine weiteren Ergebnisse vorliegen.
Damit sind alle Ergebnisse ermittelt, wenn die
Bedingung gilt: (getMoreResults ( ) = = false) & &
(getUpdateCount ( ) = = -1)
Das folgende Beispiel demonstriet die verwendung
dieser Methoden beim Aufruf einer gespeicherten
Prozedur, die mehrere ResultSets zurüchgeben
kann.:
CallableStatement stmt = con. prepareCall (“
{ call MultipleResultsProc } “); stmt. execute ( )
;
while (stmt. getMoreResults ( ) )
{ResultSet rs = stmt. getResultSet ( );
/ / ResultSet auswerten
Parameter für Ausführung von Anweisungen
Weiterhin lassen sich die Ausführung von
Anweisungen sowie die Ergebnisgröße beeinflussen.
Mit setQueryTimeout wird die maximale Zeit des Wartens auf die
Ausführung in Sekunden festgelegt, wobei der DefaultWert von 0 für unbegrenztes Warten steht. Ein
Überschreiten des gesetzten Limits wird durch
eine Exception signalisiert. Die Maximale Anzahl
von Tupeln im Anfrageergebnis wird mit der
Methode setMaxRows vordefiniert. Auch hier ist der
Default-Wert 0 und bedeutet keine Einschränkung
der Anzahl. Wird dagegen ein anderer Wert
angegeben, so werden nur die entsprechende
Anzahl von Tupeln ausgele-
LONGVARBINARY, CHAR, VARCHAR und
LONGVARCHAR relevant ist. Überschreiten die Daten das
festgelegte
Limit, so wird der Rest abgeschnitten. Der DefaultWert
von 0 bedeutet auch hier keine Beschränkung.
/ / java . sql . Statement
int getMaxFieldSize ( ) throws SQLException;
void setMaxFieldSize (int max) throws
SQLException;
int getMaxRows ( ) throws SQLException;
void setMaxRows (int max) throws SQLException;
int getQueryTimeout ( ) throws SQLException;
void getQueryTimeout (int secs ) throws
die Methode cancel durch einen anderen Thread
(Ausfüh
rungsfaden)abbrechen, wenn Treiber und DBMS dies
unterstützen.
/ / java . sql . Statement
void cancel ( ) throws SQLException;
void close ( ) throws SQLException;
Nachdem eine Anweisung ausgeführt und die
Ergebnisse
ermittelt wurden, sollten damit verbundene
Ressourcen
durch Aufruf der close-Methode freigegeben werden.
Auch
hier gilt die bereits beim Connection-Objekt
• Abbildung von SQL-Typen in java
Da SQL und Java jeweils eigene Typsysteme
besitzen, muß
eine Abbildung zwischen beiden definiert werden.
Das Pro
-blem der Typabbildung tritt in JDBC an drei Stellen
auf:
beim Zugriff auf Spaltenwerte eines Tupels mit
den getXXX-Methoden der Schnittstelle ResultSet,
bei der Übergabe von Parametern an ein
PreparedState
-ment mit den setXXX-Methoden sowie
beim Zugriff auf die OUT-Parameter eines
CallableStatements.
Binärdaten. Zur Überwindung dieser
Inkompatibilitäten sind in JDBC zunächst einige
generische Typbezeichner definiert. Diese sind in
der Klasse java . sql . Types als Konstanten
zusammengefaßt:
CHAR, VARCHAR, LONGVARCHAR für
Zeichenketten
BIT für Bitwerte und BINARY,VARBINARY,
LONGVAR
-BINARY für Binärfelder,
TINYINT, SMALLINT, BIGINT und INTEGER für
In-teger-Werte,
REAL, FLOAT und DOUBLE für
Gleitkommawerte,
DATE, TIME, TIMESTAMP für Datums- und
Umsetzung in die DBMS-spezifischen Typen
erfolgt - wenn notwendig - durch den Treiber.
Beim Auslesen von Werten aus einem ResultSetObjekt bzw. bei der Übergabe von IN-Parametern an
ein Prepared
-Statement sind dagegen die getXXX- bzw. setXXXMethoden zu verwenden. Hierbei findet ebenfalls eine
Konvertierung zwischen SQL- und Java-Typ durch den Treiber
statt. Die verwendete Abbildungsvorschrift ist in
nachfolgenden Tabelle dargestellt. Die getXXXund setXXX-Methoden sind entsprechend dem JavaTyp bezeichnet, wobei für die Byte-Felder getBytes
bzw. setBytes verwendet wird. Der in der Tabelle
angegebene Java-Typ ist auch der Typ, der beim
Aufruf von getObject zurückgegeben wird.
JDBC drei zusätzliche Klassen definiert: Date, Time
und
Timestamp. Diese Klassen kapseln im
wesentlichen die Funktionalität der Klassen aus
java . util und fügen nur noch die Behandlund der
Escape-Syntax hinzu. So kann ein Date-Objekt für
den 18. Februar 1999 über den nor- malen
Konstruktoraufruf erzeugt werden:
Date d = new Date ( 99 , 2 , 18 );
Das erste Argument bezeichnet dabei die Jahreszahl
minus 1900, d. h., für das Jahr 2000 ist
dementsprechend der Wert 100 anzugeben. Das
zweite und dritte Argument sind Monat und Tag.
Alternativ dazu kann das Date-Objekt auch durch
Aufruf der statischen Methode valueOf erzeugt
werden:
Datum in Escape-Syntax als Argument zu
übergeben. In
Ähnlicher Weise wird die Klasse java . sql .Time zur
Repräsentation der Uhrzeit (SQL-Typ Time) verwendet.
Hier ist
JDBC -Typ
Java-Typ
das Format
der Zeitangabe
hh : mm : ss.
Tabelle : Abbildung zwischen Java- und JDBC-Typen
String
. CHAR
VARCHAR
String
LONGVARCHAR
String
NUMERIC
java . math . BigDecimal
DECIMAL
java . math . BigDecimal
BIT
boolean
TINYINT
byte
SMALLINT
short
INTEGER
BIGINT
REAL
FLOAT
DOUBLE
BINARY
VARBINARY
LONGVARBINARY
DATE
TIME
TIMESTAMP
int
long
float
double
double
byte [ ]
byte [ ]
byte [ ]
java . sql . Date
java . sql . Time
java . sql . Timestamp
• BEISPIELANWENDUNG MIT JDBC
Beispiel 1: Eintragen einer Bestellung
Die Methode createOrder zum Anlegen einer
Bestellung ist etwas aufwendiger. Hier ist zunächst
die Bestellung als ein Tupel in die Relation
book_order einzutragen und für jeden Artikel dieser
Bestellung zusätzlich noch ein Tupel in die
Relation order_item einzufügen. Die einzelnen
Artikel werden dabei durch Einlesen der ISBN
sowie der Anzahl vom Benutzer erfragt. Da die
Ausführung der einzelnen Anweisungen aufgrund von Verletzungen
der Integritätsbedingungen (z. B. wenn ISBN oder
Kundennummer nicht existieren) fehlschlagen kann,
muß der gesamte Ab-
ständig erstellt werden konnte, wird das
abschließende Commit gesendet.
Programm:
void createOrder (int custId)
throws SQLException {
boolean ready = false;
preparedStatement stmt1, stmt2;
try {
con . setAutoCommit (false);
/ / Bestellung Eintragen
stmt1 = con . prepareStatement (
“ INSERT INTO book_order “ +
“ VALUES ( order_seq. NEXTVAL, ?,
“+
“ SYSDATE, 0 ) “);
stmt1 . setInt (1, custId);
stmt1 . executeUpdate ( );
stmt2 = con . prepareStatement (
“ INSERT INTO order_item “ +
“ VALUES (order_seq . CURRVAL, ?, ?)“ );
do{
/ / ISBN erfragen
String isbn = Utils . readString ( “ isbn: “);
if ( isbn = = null | | isbn . Length ( ) = = 0 )
ready = true;
else {
/ / Anzahl erfragen
int num = Utils . readInt ( “ number: “ );
if (num > 0) {
/ / Bestellposition eintragen
stmt2 . setString ( 1, isbn );
stmt2 . setInt ( 2, num);
stmt2 . ExecuteUpdate ( );
}
}
} while (! ready);
con . commit ( );
}
catch ( SQLException exc ) {
con . Rollback ( ); throws exc;}
}
Herunterladen