Datenbank-Programmierung mit JDBC Weiterbildungsmaßnahme Technische und Praktische Informatik Sommersemester 2003 DBIS @ BTU Cottbus Dipl.-Inf. Gunar Fiedler 1 Motivation für JDBC Auf dem Markt existiert eine Vielzahl verschiedener relationaler DBM-Systeme. Sie alle (bzw. die meisten) haben eines gemeinsam: sie sprechen“ SQL, d.h. sie sind in ” der Lage, in SQL formulierte Anfragen und Update-Anweisungen zu verarbeiten. Möchte man als Applikationsentwickler diese Funktionalität nutzen, muss das Programm SQL-Anweisungen generieren und an das Datenbank-Management-System schicken. Der Programmierer steht dabei aber vor dem Problem, dass für die einzelnen Systeme über die Zeit hinweg unabhängig voneinander unterschiedliche Zugriffsmechanismen entwickelt wurden. Dies bedingt, dass man sich schon zu Beginn eines Softwareprojekts auf ein bestimmtes DBMS, ja sogar auf eine bestimmte Version festlegen müsste. Ein Wechsel des verwendeten DBMS bedeutet in diesem Fall umfangreiche Änderungen im Code der Applikation. Dem Credo der Datenbanken folgend sollte es aber möglich sein, die Verarbeitungslogik der Anwendung so weit wie möglich von der Datenbank abzukoppeln. Um dies zu erreichen, bedient man sich einer Treiber-Architektur. Die Anwendung wird von der Datenbank durch eine weitere Softwarekomponente, Treiber genannt, getrennt. Ein Treiber stellt der Anwendung eine für alle DBMS einheitlich definierte Schnittstelle zur Verfügung und implementiert intern die Protokolle für ein ganz bestimmtes System. Soll nun das DBMS ausgetauscht werden, so muss lediglich ein anderer Treiber in die Anwendung eingebunden werden. Umgedreht ist der Treiber unabhängig von der Applikationslogik und kann auch in anderen Anwendungen wiederverwendet werden. Mit der Java Database Connectivity, kurz JDBC, steht Java-Programmierern eine standardisierte Umgebung für den Zugriff auf beliebige relationale Datenbanken zur Verfügung. Zu diesem Zweck definiert JDBC eine Reihe von Schnittstellen und Mechanismen, die automatisch in allen Java-Entwicklungs- und Laufzeitumgebungen zur Verfügung stehen. 2 Grundgerüst eines JDBC-basierten Programms JDBC-Grundlagen .. ...... ...... ...... ...... .. ...... ...... ...... ...... Applikationslogik .. ...... ...... ...... ...... .......................................................................................................................................... .. ...... ...... ...... ...... .. ...... ...... ...... ...... JDBCTreiber .. ...... ...... ...... ...... .. ...... ...... ...... ...... JDBCTreiber .. ...... ...... ...... ...... .. ...... ...... ...... ...... .. ...... ...... ...... ...... JDBCTreiber .. ...... ...... ...... ...... .. ...... ...... ...... ...... .. ...... ...... ...... ...... .. ...... ...... ...... ...... ODBCTreiber . ...... ...... ...... ...... .......................................................................................................................................... .............................. .................... ......... ......... ...... ...... .... ... .... ..... . .... .. ...... ..... ......... ....... . . . . . . .................. . . . .............................. Sybase ...... . ..... ...... ...... ....... ....... ........... .................................. .............................. .................... ......... ......... ...... ...... .... ... .... ..... . .... .. ...... ..... ......... ....... . . . . . . .................. . . . .............................. Oracle ...... ...... ...... ...... ....... ....... ........... .................................. .............................. .................... ......... ......... ...... ...... .... ... .... ..... .. .... .. ...... ..... ......... ....... . . . . . . .................. . . . .............................. CSV-Datei ...... ...... ...... ...... ....... ....... ........... .................................. Abbildung 1: Aufbau einer JDBC-basierten Applikation 2 Grundgerüst eines JDBC-basierten Programms Abbildung 1 zeigt den Grobaufbau einer JDBC-basierten Applikation. Sie besteht aus 3 Schichten: • Applikationslogik: Sie definiert die Verarbeitungsregeln der Geschäftsprozesse. • JDBC-Datenbanktreiber: Er kapselt die Spezifika des Datenbanksystems und stellt der Applikationslogik die Daten der Datenbank mit Hilfe einer standardisierten Schnittstelle zur Verfügung. • Datenbank: Speichert die Daten. Der Treiber kommuniziert mit der Datenbank über ein DBMS-spezifisches Protokoll. Aus Sicht des Java-Programmierers ist ein JDBC-Datenbanktreiber ein Package bestehend aus einer Reihe von Klassen. Die Klassen implementieren die Interfaces des Standard-Packages java.sql. Diese Interfaces erlauben das datenbankunabhängige Programmieren mit Hilfe von JDBC: die Anwendung sieht“ nur ein java.sql” Interface, welche Klasse aus welchem Treiber letztlich instanziiert wurde, bleibt verborgen. 2 SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus JDBC-Grundlagen 2 Grundgerüst eines JDBC-basierten Programms Der Zugriff auf eine Datenbank über JDBC innerhalb eines Java-Programms lässt sich in einzelne Phasen aufteilen, die nacheinander ausgeführt werden müssen, Abbildung 2 zeigt eine Übersicht. pppppp Laden des Treibers ppppppppp ppppp Herstellen der Verbindung zum DBMS pppppppp ppppp Erzeugen eines Statement-Objekts ppppppppp ppp pppw ppppppppppppp ppppppppppppppppppppppppppppp p p p p p p p p p p p ppppppppppppppp ppp ppppppppppppppp ppppppppppppp p p p p p p p p p p ppppppppppppppp p p pppp ppppppppppppppp pp pppppppppp p p p p p p p ppppppppppppppp p p p p p p pppppppppp ppppppppppppppppp Ausführen einer Anfrage w Ausführen eines Updates ppp pp Auswertung der Ergebnisse p pppppppppp pppp pppppppp pppppppppp ppp ppppppppp pppppppppp pp pppppppppp p p p p p p pppppppppp p p p p p ppppp pppppppppp pppp pppppppp pppppppppp ppp ppppppppp pppppppppp pp pppppppppp p pppppppppp p p p p p p p p p p pppppppppp ppppppppppp ppppppppppw pppppppppp pp w ppppp Schließen der Verbindung Abbildung 2: Phasen des Datenbankzugriffs 1. Laden des Treibers: In dieser Phase werden die Klassen des JDBC - Treibers dynamisch in die Anwendung eingebunden und registriert. 2. Herstellen der Verbindung: Der Treiber baut eine Verbindung zum DBMS auf. 3. Erzeugen eines Statements: Statements sind die JDBC-Repräsentation für SQL-Anweisungen. Bevor ei- SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus 3 3 Die Phasen im Detail JDBC-Grundlagen ne SQL-Anfrage gestartet werden kann, muss sie in ein Statement verpackt“ ” werden. 4. Ausführen des Statements: Die SQL-Anfrage wird an das DBMS übermittelt und dort ausgeführt. Die Resultate werden zurückgeliefert. 5. Auswertung der Ergebnisse: Über die Methoden des Interfaces java.sql.ResultSet kann das Ergebnis einer Anfrage ausgewertet werden. 6. Schließen der Verbindung: um nicht weiter benötigte Ressourcen freizugeben. 3 Die Phasen im Detail Dieser Abschnitt verdeutlicht die einzelnen Phasen eines JDBC-Zugriffs anhand von Codebeispielen und Abbildungen. Grundlage bildet wieder unsere 3-SchichtenAnwendung: .. ...... ...... ...... ...... .. ...... ...... ...... ...... Applikationslogik .. ...... ...... ...... ...... .......................................................................................................................................... .. ...... ...... ...... ...... .. ...... ...... ...... ...... Sybase-Treiber .. ...... ...... ...... ...... .......................................................................................................................................... ................................. .................. ......... ......... ...... ...... .... .... ... ..... .. .... .... ...... ......... ...... .................. ......... ................................. Sybase ...... ...... ...... ...... ....... ....... ........... ................................... 3.1 Laden des Treibers Wie bereits angedeutet, ist ein JDBC-Datenbanktreiber nichts anderes als ein Package diverser Java-Klassen, welche die Interfaces aus java.sql implementieren. Jeder Treiber enthält als Repräsentanten eine Hauptklasse“. Über diese Klasse wird der ” Treiber identifiziert und angesprochen. Eine zentrale JDBC-Komponente ist der Treibermanager. Er wird durch die Klasse java.sql.DriverManager repräsentiert und ist zuständig für die Verwaltung und den Zugriff auf die Datenbanktreiber. Ein Treiber steht der Anwendung erst zur Verfügung, wenn er sich beim Treibermanager registriert hat. 4 SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus JDBC-Grundlagen 3 Die Phasen im Detail Das dynamische Laden eines Treibers gestaltet sich genauso wie das Laden jeder anderen beliebigen Java-Klasse. Angenommen, wir möchten den Treiber für Sybase-Datenbanken laden. Die Hauptklasse“ des JDBC-Treibers hat den Namen ” com.sybase.jdbc2.jdbc.SybDriver: try { C l a s s . forName ( ”com . s y b a s e . j d b c 2 . j d b c . S y b D r i v e r ” ) ; } catch ( C l a s s N o t F o u n d E x c e p t i o n e ) { // F e h l e r b e h a n d l u n g , f a l l s K l a s s e n i c h t g e f u n d e n wurde } Die Treiberklasse sorgt automatisch für eine Registrierung beim Treibermanager. Das Laden des Treibers muss nur einmal während der Initialisierungsphase durchgeführt werden, der Treiber steht bis zum Ende der Applikation zur Verfügung: . ...... ...... ...... ...... .. ...... ...... ...... ...... Applikationslogik .. ...... ...... ...... ...... .......................................................................................................................................... .. ...... ...... ...... ...... .. ...... ...... ...... ...... Sybase-Treiber .. ...... ...... ...... ...... .......................................................................................................................................... ............................... ................... ......... ......... ...... ...... .... .... ... ..... ... .... .... ...... ...... ......... . . . . . . .................. .... ................................. Sybase ...... . ...... ..... ....... ...... ........... ....... .................................. 3.2 Herstellen der Verbindung Nachdem der Datenbank-Treiber in die Anwendung integriert wurde, kann die Verbindung zur Datenbank geöffnet werden. Dafür sind die folgenden Informationen von Bedeutung: • Welcher Treiber soll benutzt werden? • Wie heißt die Datenbank und wo befindet sie sich? • Mit welchen Daten authentifiziere ich mich gegenüber der Datenbank? • ... SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus 5 3 Die Phasen im Detail JDBC-Grundlagen Das Öffnen der Verbindung wird durch den Treibermanager und dessen statische Methode getConnection() veranlasst. Diese Methode liefert ein Objekt zurück, dessen Klasse das Interface java.sql.Connection implementiert. Die oben genannten Daten werden der Methode in Form einer URL (uniform resource locator) übergeben. Diese URL hat die folgende standardisierte Form: jdbc:<subprotocol>:<subURL> Über die Zeichenkette <subprotocol> wird der gewünschte Treiber spezifiziert. Der Inhalt von <subURL> hängt vom konkreten Treiber ab. Der Sybase-Treiber erwartet beispielsweise folgende URL: jdbc:sybase:Tds:<host>:<port>/<database>?<parameter> Die Kombination aus <host> und <port> gibt dabei den Server an, auf dem sich die Datenbank befindet, <database> spezifiziert den Namen der Datenbank. Zusätzliche Parameter können in der Form <name>=<wert> nach dem Fragezeichen angegeben werden. Möglich sind z.B. die Authentifizierungsdaten (user und password), auch der Name der Datenbank kann alternativ als Parameter servicename übergeben werden. Angenommen, der Nutzer joe (Passwort: foo) möchte die Sybase-Datenbank namens Personal auf dem Rechner dbserver nutzen. Das DBMS ist über Port 1234 erreichbar. Es gibt 3 verschiedene Varianten der DriverManager.getConnection-Methode: • DriverManager.getConnection(String url): Der allgemeine Fall. Alle Daten müssen gemäß der oben genannten Vorschrift in eine URL verpackt werden: String url = ”j d b c . s y b a s e : Tds : d b s e r v e r : 1 2 3 4 ” + ”/ P e r s o n a l ? u s e r=j o e&p a s s w o r d=f o o ” ; C o n n e c t i o n conn = DriverManager . getConnection ( u r l ) ; • DriverManager.getConnection(String url, Properties info): Der Methode wird zusätzlich ein assoziatives Array info übergeben, welches die Parameter aufnehmen kann, die normalerweise nach dem Fragezeichen der URL stehen würden. Sinnvoll ist diese Trennung, wenn diese Parameter dynamisch generiert werden. Dann entfällt die umständliche Kodierung der URL: P r o p e r t i e s p r o p s = new P r o p e r t i e s ( ) ; p r o p s . pu t ( ”u s e r ” , ”j o e ” ) ; p r o p s . pu t ( ”p a s s w o r d ” , ”f o o ” ) ; p r o p s . pu t ( ”s e r v i c e n a m e ” , ”P e r s o n a l ” ) ; S t r i n g u r l = ”j d b c : s y b a s e : Tds : d b s e r v e r : 1 2 3 4 ” ; 6 SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus JDBC-Grundlagen 3 Die Phasen im Detail C o n n e c t i o n conn = DriverManager . getConnection ( url , props ) ; • DriverManager.getConnection(String url, String user, String password): Analog zur 2. Variante, falls als Parameter nur Nutzername und Passwort benötigt werden: String url = ”j d b c . s y b a s e : Tds : d b s e r v e r : 1 2 3 4 / P e r s o n a l ” ; C o n n e c t i o n conn = D r i v e r M a n a g e r . g e t C o n n e c t i o n ( u r l , ”j o e ” , ”f o o ” ) ; Nach Abschluss dieser Phase besteht eine Kommunikationsverbindung zwischen Applikation und der Datenbank: .. ...... ...... ...... ...... . ...... ...... ...... ...... Applikationslogik .. ...... ...... ...... ...... .......................................................................................................................................... ... ......... ......... ......... ... ......... ......... ......... Sybase-Treiber ... ......... ......... ......... qqqq qq qqqqq qqqqq .......................................................................................................................................... .............................. .................... ......... ......... ...... ...... .... .... ... ..... ... .... .... ...... ..... ......... ......... .................. . . . . . . . . . . . . . . . ................... qqq Sybase ...... . ..... ...... ....... ...... ........... ....... .................................. 3.3 Erzeugen eines Statements SQL-Anfragen werden unter JDBC in Statement-Objekte eingekapselt. Bevor eine Anfrage ausgeführt werden kann, muss ein derartiges Objekt erzeugt werden. Dies geschieht durch Aufruf der createStatement-Methode des im vorherigen Schritt erzeugten Connection-Objekts: S t a t e m e n t stmt = conn . c r e a t e S t a t e m e n t ( ) ; SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus 7 3 Die Phasen im Detail JDBC-Grundlagen .. ...... ...... ...... ...... .. ...... ...... ...... ...... Applikationslogik .. ...... ...... ...... ...... .......................................................................................................................................... ... ......... ......... ......... ... ......... ......... ......... Sybase-Treiber ................................................. .............. ........ ....... ..... ..... .. .. .... ....... ............. .................................................... qqqqqqqqqqqqqq................ stmt ... ......... ......... ......... qqq qqq qqqqq qqqq .......................................................................................................................................... .. ........................ ................................ ...... ......... ..... ...... .... ... ..... . .... ... ...... .... ......... ...... . . . . . . . . .................. ................................. qqqq Sybase ...... ...... ...... ...... ....... ....... ........... ................................... Neben einfachen SQL-Anfragen, wie wir sie hier betrachten, können über StatementObjekte auch spezielle vorcompilierte Anfragen (prepared statements) und stored procedures ausgeführt werden. Außerdem lässt sich über die Statement-Objekte die Rückgabe des Ergebnisses steuern. Zu beachten ist: ein einzelnes Statement kann nicht mehrere SQL-Anfragen gleichzeitig verwalten. Ergebnisse müssen verarbeitet werden, bevor eine neue Anfrage gestellt wird. 3.4 Ausführen eines Statements Das Statement-Objekt stellt Methoden zum Ausführen von SQL-Anweisungen bereit. Aufgrund der unterschiedlichen Bedürfnisse werden zwei verschiedene Anweisungsarten unterschieden: Anfragen und Updates. Anfragen sind SQL-Anweisungen, die ein Ergebnis zurückliefern, typischerweise select - Anweisungen. Sie werden über die executeQuery-Methode des StatementObjekts ausgeführt, diese Methode nimmt die Anfrage als Parameter in Textform entgegen und liefert das Ergebnis in einem ResultSet-Objekt zurück: ResultSet re s ult = stm t . e x e c u t e Q u e r y ( ”SELECT ∗ FROM s t a f f ” ) ; 8 SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus JDBC-Grundlagen 3 Die Phasen im Detail .. ...... ...... ...... ...... .. ...... ...... ...... ...... Applikationslogik .... ...... ...... ...... ..... pp p p p p ppppppppppp p pp p p p p p p p p p ppp p p p p p ppp pp p p p . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .p p.pp p. . . . . . . . . . .pp.p . . . . . . . . . . . . . . . . . . . . . . . . . . . . ................................... p p pp pp p ........ pp ................. ...... pp p ........ ppp ..... ... p p p ..... p result ... pp pp ..... .... p . ........ . . p . . . pppp pppp ... ................ ...................................... ppp pppp p p ppp p pppppp ... ... ......... ......... ......... ......... ............................................................... ......... ......... .......... ...... ...... .. ..... qqqqqqqqqqqqqq............ stmt ............... . .............. ...........................p.p..................... Sybase-Treiber p p p ... ......... ppppppp pppppp ......... ......... pp pp p qqq pp ppp ppp pp ppp ppp p pp pp ppp pp p p . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .qq. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .pp. . . . . . . . . q p ppp q q p ppp pp p qqq qqqq pp pp p ... p pp p p .... ... .. .............................................. pp p pp ............ ........ p ........ p ..... p . . . p p . ... qqqq ... pp .. ..... ppp p ... .... pp p p p .... ...... p pp p p ...... ......... pp p p p p ......... .................. p pp p p p p p ................................. p p p p p pp p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p p pp p p p pp p p p p p p p p p p p p p p p p pp p p p p p p pp p p ppppppppp pppppp ppppp pppppp ppppppppppppp pppppppppppppppppppppppppppppppppp Sybase SELECT * FROM staff ...... ...... ...... ...... ....... ....... ........... ................................... Updates entsprechen den drei DML-Anweisungen insert, update und delete. Sie werden über die executeUpdate-Methode des Statement-Objekts ausgeführt. Diese Methode liefert als Ergebnis die Anzahl der durch das Update betroffenen Tupel zurück, vorausgesetzt der Treiber sowie das DBMS unterstützen dies: stmt . executeUpdate ( ”UPDATE s t a f f SET s a l a r y =3000 WHERE name=’ Joe Hacker ’ ” ) ; .... ...... ...... .... ...... ...... Applikationslogik .... ...... ..... ..... ..... .... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ........ . . . . . . . . . . . . . . . . ... ... ... ... .. ........ . . . . . . . . . . . . . . . .. .. ......... ......... ............................................ . . . . . . ... ..... ... ........ .......................................... ... . ... ......... .. ......... ... . .. .. ... ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ...... . . . . . . . . . . . . . . . . .. ... .. ... ....... .... ................ ..................... .... ... ...... ..... . . ..... . . . . . . ...... ...... ...... .............. ........ ......................... ......... ............ ....................................... Sybase-Treiber ppppppppp stmt ppp pp p pp ppp ppp Sybase ...... .. .......... ....... ............................. 3.5 UPDATE staff SET salary=3000 WHERE name=’Joe Hacker’ Auswertung der Ergebnisse Wurde eine Anfrage ausgeführt, muss in dieser Phase das zurückgelieferte ResultSet ausgewertet werden. Dies geschieht nach dem Iterator-Prinzip. Das Objekt besitzt einen internen Zeiger auf das jeweils aktuelle Tupel der Ergebnismenge. Über die Methode next() wird dieser Zeiger auf das jeweils nächste Tupel gesetzt. Ist kein Tupel mehr vorhanden, so liefert die next-Methode den boolschen Wert false zurück. Auf die einzelnen Attribute des aktuellen Tupels kann über die getXXX-Methoden des ResultSet-Objekts zugegriffen werden. XXX steht dabei für die Namen der Standarddatentypen, z.B. String oder Double. Diese Methoden erwarten entweder den SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus 9 4 Ein zusammenhängendes Beispiel JDBC-Grundlagen Index des Attributs (Achtung: die Zählung beginnt bei 1!) oder dessen Namen. Es wird vorausgesetzt, dass der Typ des jeweiligen Attributs bekannt ist. while ( r e s u l t . next ( ) ) { System . o u t . p r i n t l n ( r e s u l t . g e t S t r i n g ( ”name ”)+ ” : ”+ Double . t o S t r i n g ( r e s u l t . g e t D o u b l e ( ” s a l a r y ” ) ) ) ; } 3.6 Schließen der Verbindung Wird eine Datenbank-Verbindung nicht mehr benötigt, kann sie durch einen Aufruf der close-Methode des Connection-Objekts geschlossen werden, um Systemressourcen freizugeben: conn . c l o s e ( ) ; .. ...... ...... ...... ...... .. ...... ...... ...... ...... Applikationslogik .. ...... ...... ...... ...... .......................................................................................................................................... .... ......... ......... ......... .... ......... ......... ......... Sybase-Treiber .... ......... ......... ......... .......................................................................................................................................... ......................................... .............. ........ ........ ...... ...... ... ... .. ..... ... .... ... ...... ...... . ......... . . . . . . .................... ... .............................. Sybase ...... ...... ...... ....... ...... ........... ....... .................................. 4 Ein zusammenhängendes Beispiel Nachfolgend sind die Codebeispiele des letzten Abschnitts nochmal in einem Block zusammengefasst: // I m p o r t d e r I n t e r f a c e s d e s j a v a . s q l −P a c k a g e s import j a v a . s q l . ∗ ; import j a v a . u t i l . P r o p e r t i e s ; p u b l i c c l a s s JDBCMain { 10 SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus JDBC-Grundlagen 4 Ein zusammenhängendes Beispiel p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { try { // Laden d e s Sybase −T r e i b e r s C l a s s . forName ( ”com . s y b a s e . j d b c 2 . j d b c . S y b D r i v e r ” ) ; } catch ( C l a s s N o t F o u n d E x c e p t i o n e ) { e . printStackTrace () ; System . e r r . p r i n t l n ( ”T r e i b e r n i c h t g e f u n d e n ! ” ) ; System . e x i t ( −1); } try { P r o p e r t i e s p r o p s = new P r o p e r t i e s ( ) ; // Das s o l l t e n a t u e r l i c h vom N u t z e r a b g e f r a g t werden ! p r o p s . pu t ( ”u s e r ” , ”j o e ” ) ; p r o p s . pu t ( ”p a s s w o r d ” , ”f o o ” ) ; p r o p s . pu t ( ”s e r v i c e n a m e ” , ”P e r s o n a l ” ) ; S t r i n g u r l = ”j d b c : s y b a s e : Tds : d b s e r v e r : 1 2 3 4 ” ; // O e f f n e n d e r V e r b i n d u n g zum DBMS C o n n e c t i o n conn = D r i v e r M a n a g e r . g e t C o n n e c t i o n ( u r l , p r o p s ) ; // E r z e u g e n e i n e s S t a t e m e n t s S t a t e m e n t stmt=conn . c r e a t e S t a t e m e n t ( ) ; // A u s f u e h r e n e i n e s U p d a t e s String sql = ”UPDATE s t a f f SET s a l a r y =3000 WHERE name=’ Joe Hacker ’ ” ; s tm t . e x e c u t e U p d a t e ( s q l ) ; // A u s f u e h r e n e i n e r A n f r a g e s q l = ”SELECT ∗ FROM s t a f f ” ; R e s u l t S e t r e s u l t = stmt . e x e c u t e Q u e r y ( s q l ) ; // A u s w e r t e n d e r A n f r a g e −E r g e b n i s s e while ( r e s u l t . next ( ) ) { System . o u t . p r i n t l n ( r e s u l t . g e t S t r i n g ( ”name ”)+ ” : ”+ Double . t o S t r i n g ( r e s u l t . g e t D o u b l e ( ” s a l a r y ” ) ) ) ; } // Beenden d e r V e r b i n d u n g SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus 11 5 Weitergehende Möglichkeiten mit JDBC JDBC-Grundlagen conn . c l o s e ( ) ; } catch ( E x c e p t i o n e ) { // Ausgabe d e r F e h l e r m e l d u n g e . printStackTrace ( ) ; System . e x i t ( −1); } } } 5 Weitergehende Möglichkeiten mit JDBC Die Möglichkeiten von JDBC gehen weit über die hier vorgestellten Grundlagen hinaus. Dieser Abschnitt stellt als Beispiele einige fortgeschrittene Techniken vor: Abfrage der Schema-Information, die Bearbeitung von Ergebnismengen und Transaktionen. 5.1 Nutzung von Metadaten Bisher wurde vorausgesetzt, dass der Programmierer das Schema der Datenbank genau kennt. Wenn z.B. der Typ eines Attributs eines Tupels der Ergebnismenge unbekannt ist, weiß der Entwickler nicht, welche getXXX-Methode zu benutzen ist. Diese Schema-Informationen (Metadaten) sind allerdings im Data Dictionary der Datenbank gespeichert und können über JDBC abgefragt werden. Metadaten stehen an zwei Stellen zur Verfügung: • Connection.getMetaData liefert ein DatabaseMetaData - Objekt zurück, das Informationen über die Datenbank im Allgemeinen enthält, d.h. die Tabellenstruktur, die gespeicherten Prozeduren, den verwendeten SQL-Dialekt, etc. • ResultSet.getMetaData liefert ein ResultSetMetaData - Objekt zurück, das Informationen über die Struktur einer Ergebnismenge enthält, also z.B. Namen und Typen der Attribute. Das folgende Code-Fragment (aufbauend auf der oben genutzten Personal-Datenbank) analysiert die Attribut-Struktur der Ergebnismenge einer Anfrage: 12 SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus JDBC-Grundlagen 5 Weitergehende Möglichkeiten mit JDBC /∗ . . . ∗/ // A n f r a g e a u s f u e h r e n R e s u l t S e t r e s u l t = stmt . e x e c u t e Q u e r y ( ”SELECT ∗ FROM s t a f f ” ) ; // Metadaten a u s l e s e n R e s u l t S e t M e t a D a t a md = r e s u l t . getMetaData ( ) ; // Ausgabe a l l e r A t t r i b u t n a m e n // ACHTUNG : d a s e r s t e A t t r i b u t h a t d i e P o s i t i o n 1 ! f o r ( i n t i d x = 1 ; i d x <=md . getColumnCount ( ) ; i d x ++) { System . o u t . p r i n t l n (md . getColumnName ( i d x ) ) ; } /∗ . . . ∗/ 5.2 Verarbeitung von Ergebnismengen Ein normales ResultSet-Objekt hat zwei einschränkende Eigenschaften: • Die Werte können nicht verändert werden. • Sie besitzen ein sogenanntes forward-only-Cursor, d.h. die Menge kann nur genau einmal vom ersten bis zum letzten Element durchlaufen werden. Seit JDBC Version 2.0 existieren scrollbare ResultSets. Mittels diverser Navigationsmethoden kann der Zeiger innerhalb der Menge beliebig gesetzt werden: auf absolute Positionen, vorwärts oder rückwärts. Außerdem ist es wünschenswert, wenn man die Daten eines ResultSets nachbearbeiten kann. Zu diesem Zweck existieren änderbare ResultSet-Objekte mit updateXXXMethoden. Der Typ eines ResultSets wird durch das Statement bestimmt, in dessen Kontext es erzeugt wird. Ein Statement, das mittels conn.createStatement() erzeugt wurde, generiert stets nicht veränderbare und nicht scrollbare ResultSets. Werden der Methode zwei Parameter übergeben, lässt sich dieses Verhalten ändern: S t a t e m e n t stmt = conn . c r e a t e S t a t e m e n t ( i n t r e s u l t S e t T y p e , int resultSetConcurrency ); Der Parameter resultSetType kann folgende Werte annehmen: SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus 13 5 Weitergehende Möglichkeiten mit JDBC resultSetType ResultSet.TYPE_FORWARD_ONLY ResultSet.TYPE_SCROLL_INSENSITIVE ResultSet.TYPE_SCROLL_SENSITIVE JDBC-Grundlagen Bedeutung Das ResultSet kann nur einmal vom ersten bis zum letzten Element durchlaufen werden. Das ResultSet ist scrollbar, simultane Änderungen anderer Nutzer bleiben verborgen Das ResultSet ist scrollbar, Änderungen anderer Nutzer schlagen auf die Werte durch. Für resultSetConcurrency gibt es diese Möglichkeiten: resultSetConcurrency ResultSet.CONCUR_READ_ONLY ResultSet.CONCUR_UPDATABLE Bedeutung Das Ergebnis kann nicht verändert werden. Das Ergebnis kann editiert werden. Das folgende Code-Fragment erzeugt ein Statement für scrollbare und änderbare ResultSets: /∗ . . . ∗/ S t a t e m e n t s tmt = conn . c r e a t e S t a t e m e n t ( R e s u l t S e t . TYPE SCROLL INSENSITIVE , R e s u l t S e t . CONCUR UPDATABLE ) ; /∗ . . . ∗/ // a u s f u e h r e n e i n e r A n f r a g e ResultSet re s ult = stm t . e x e c u t e Q u e r y ( ”SELECT ∗ FROM s t a f f ” ) ; // s e t z t den Z e i g e r a u f P o s i t i o n 2 result . absolute (2); // s e t z t den Z e i g e r a u f d a s e r s t e T u p e l result . first (); // a e n d e r t d a s G e h a l t r e s u l t . updateDouble ( ”s a l a r y ” ,2000); 5.3 Transaktionen Die JDBC-API unterstützt die Nutzung von Transaktionen. Standardmäßig ist definiert, dass jede SQL-Anweisung, die per executeQuery oder executeUpdate ausgeführt wird, einer Transaktion entspricht. Dieses Verhalten lässt sich über die setAutoCommit-Methode des Connection-Objekts ändern. Durch den Parameter 14 SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus JDBC-Grundlagen 6 JDBC und Applets false wird nach Ausführung von executeXXX-Methoden ein expliziter Aufruf von Connection.commit bzw. Connection.rollback erwartet: /∗ . . . ∗/ // e i n e T r a n s a k t i o n f u e r s i c h stmt . executeUpdate ( ”UPDATE s t a f f SET s a l a r y =3000 WHERE name=’ Joe Hacker ’ ” ) ; conn . setAutoCommit ( f a l s e ) ; /∗ ∗∗∗ ∗ Beginn der T r a n s a k t i o n ∗∗∗ ∗/ stmt . executeUpdate ( ”DELETE FROM s t a f f WHERE name=’ A l f Weise ’ ” ) ; stmt . executeUpdate ( ”INSERT INTO s t a f f ( name , s a l a r y ) VALUES ( ’ Tina K u n t e r b u n t ’ , 1 0 0 0 ) ” ) ; // Commit conn . commit ( ) ; /∗ ∗∗∗ ∗ Ende d e r T r a n s a k t i o n ∗∗∗ ∗/ /∗ ∗∗∗ ∗ B e g i n n e i n e r neuen T r a n s a k t i o n ∗∗∗ ∗/ stmt . executeUpdate ( ”UPDATE s t a f f SET s a l a r y =2000 WHERE name=’ Tina K u n t e r b u n t ’ ” ) ; // R o l l b a c k conn . r o l l b a c k ( ) ; /∗ ∗∗∗ ∗ Ende d e r T r a n s a k t i o n ∗∗∗ ∗/ /∗ . . . ∗/ Hinweis: Das zugrunde liegende DBMS muss natürlich Transaktionen unterstützen! 6 JDBC und Applets Es existieren zwei verschiedene Anwendungsbereiche für Java: Applikationen, die als normale“ Programme auf einem Rechner laufen und Applets, deren Code von einem ” SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus 15 6 JDBC und Applets JDBC-Grundlagen dbserver ppppp ppppp pp pp ppppp p p p p p ppppp pppp ppppp ppp pp JDBCTreiber Port 1234 Sybase Datenbankserver pp verboten pppp p ppp pp pp ppp p p p p ppppp pppp p ppp pp pp ppp p p p p pppp Client Applet pppppppp pppppppp p ppppppp ppppppppppppppp ppppppppppppppp ppppppppppppppp ppppppppppppppp ppppppppppppppp ppppppppppppppp Port ppppppppppppppp pppppppppp 80 www Apache Webserver Abbildung 3: direkter JDBC-Zugriff (verboten) www Client pppppppp Applet JDBCTreiber pppppppp ppppp ppppppppppppp ppppppppppppppppppppppppppppppp pppppppppppppppppppppppppp pppp pppppppppppppppp pppppppppppppp ppppppppppppppppppppppppppppppppppppppppp ppppppppppppppppppppppppppppppppppppppppp pppppppppppppppppppppppppppp Port Apache 80 Webserver pppppppp Port Sybase 1234 Datenbankserver Abbildung 4: Web- und Datenbankserver auf einem Rechner anderen Rechner geladen wird und deshalb bestimmten Sicherheitsanforderungen genügen muss. Eine wichtige Einschränkung ist, dass ein Applet nur Daten von dem Rechner lesen darf, von dem aus es geladen wurde. Der Zugriff auf die lokalen Daten des Clienten sind verboten, außerdem darf das Applet keine Verbindungen zu anderen Rechnern aufbauen. Die Konsequenz für ein JDBC-nutzendes Applet zeigt Abbildung 3: wenn das Applet vom Rechner namens www geladen wurde, ist keine direkte Datenbankverbindung zum Rechner dbserver möglich. Es bestehen zwei Möglichkeiten, dieses Problem zu lösen. Zum Ersten sind Datenbankzugriffe gestattet, wenn sich der Datenbankserver auf demselben Rechner befindet wie der Webserver, siehe Abbildung 4. Diese Konstellation hat allerdings einige Nachteile: die Last des Webservers und des Datenbankservers konzentrieren sich auf einen gemeinsamen Rechner. Außerdem lässt sich der Datenbankserver nur schlecht abschirmen, wenn z.B. nur Zugriffe aus dem Intranet erlaubt sein sollen. Aus diesem Grund wird oftmals die zweite Lösung favorisiert, siehe Abbildung 5. Auf dem Webserver wird ein Gateway eingerichtet. Dieses Gateway hat die Aufgabe, 16 SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus JDBC-Grundlagen 7 Weiterführende Lektüre www Client Applet JDBCTreiber pppppppp pppppppp pppppppp ppppppppppppp pppppppp ppppppp pppppppppppppp p pppppppppppppppppppp ppppppppppppppppppppp pppppppppppp pppppppppppp pppppppppppp pppppppppppp pppppppppppp pppppppppppp pppppppppppp pppppppppppp p Port 80 Apache Webserver pppppppp Gateway p pp pp dbserver pppppppp Port Sybase 1234 Datenbankserver Abbildung 5: JDBC-Zugriff über ein Gateway Anfragen von außen entgegenzunehmen, evtl. vorzuverarbeiten und die Datenbankzugriffe an den eigentlichen Server weiterzuleiten. Ein weiteres Problem ist der JDBC-Treiber an sich. Zur Erinnerung: Applets sind Programme, die auf einem Client-Rechner ausgeführt werden. Ein JDBC-Treiber ist nichts weiter als eine Menge von Java-Klassen, deren Code ebenfalls auf dem Clienten ausgeführt wird. Aus Sicherheitsgründen sind deshalb nicht alle JDBC-Treiber für den Einsatz in Applets geeignet. An dieser Stelle sei allerdings auf weiterführende Literatur verwiesen. 7 Weiterführende Lektüre Im WWW sind eine Vielzahl von Dokumenten über den Gebrauch von JDBC verfügbar, z.B.: • Java-API Dokumentation: http://java.sun.com/products/jdk/1.4.2/docs/api/index.html • JDBC-Tutorial von Sun: http://java.sun.com/docs/books/tutorial/jdbc/ • ein weiteres Sun-Tutorial: http://developer.java.sun.com/developer/Books/JDBCTutorial/ • Kurzübersicht: http://www.cs.unc.edu/Courses/wwwp-s98/members/thornett/jdbc/ SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus 17 7 Weiterführende Lektüre JDBC-Grundlagen • Code-Beispiele für Java allgemein: http://javaalmanac.com/ • Ein JDBC-Seminar: http://www.informatik.uni-stuttgart.de/ipvr/as/lehre/seminar/docss02/ JDBC-Seminar.pdf • ... 18 SS 2003 Gunar Fiedler, LS DBIS, BTU Cottbus