Java und Datenbanken Benutzerschnittstelle DB-Client Client Benutzerschnittstelle CORBA, HTTP,... DB-Client Anwendungslogik DB-Schnittstelle JDBC, SQLJ Anwendungslogik DB-Schnittstelle Anwendungs- Server DBMS-Protokoll DBMS-Protokoll DB-Server DB DB DB-Server Benutzerdefinierte Prozeduren, Funktionen & Datentypen in Java 153
JDBC Entwicklung von Sun JDBC 1 definiert die Basisfunktionalität von JDBC Herstellen von Verbindungen zu SQL-Datenbanken Ausführen von SQL-Anweisungen Verarbeiten von Anfrageergebnissen JDBC 2 erweitert JDBC 1 um scroll- und änderbare Ergebnismengen Batch-Änderungen SQL:1999-Datentypen benutzerdefinierte Typabbildungen JDBC 3 unterstützt weitere SQL:1999-Funktionalität Sicherungspunkte Änderungsmethoden für Spalten von konstruierten bzw. benutzerdefinierten Datentypen 154
JDBC - Überblick Datenbankzugriffsschnittstelle für Java abstrakt und datenbankneutral vergleichbar mit ODBC Low-Level-API: direkte Nutzung von SQL Java-Package java.sql Klassen DriverManager: Einstiegspunkt, Laden von Treibern Connection: Datenbankverbindung Statement: Ausführung von Anweisungen über eine Verbindung ResultSet: verwaltet Ergebnisse einer Anfrage, Zugriff auf einzelne Spalten 155
Datenbankanbindung - Ablauf Aufbau einer Verbindung zur Datenbank Angabe der Verbindungsinformationen Auswahl und dynamisches Laden des Treibers Senden einer SQL-Anweisung Definition der Anweisung Belegung von Parametern Verarbeiten der Anfrageergebnisse Navigation über Ergebnisrelation Zugriff auf Spalten DriverManager getconnection() Connection createstatement() Statement executequery() ResultSet getxxx() XXX 156
import java.sql.*; import java.util.*; Einfaches JDBC-Beispiel Beispiel (in Oracle) class Kundennamen { public static void main (String args []) throws SQLException { // Lade Oracle JDBC Treiber und stelle Verbindung zur Datenbank ORDB her DriverManager.registerDriver(new oracle.jdbc.driver.oracledriver()); Connection con = DriverManager.getConnection("jdbc:oracle:oci8:@", "scott", "tiger"); } } // Erzeuge und führe SQL-Anweisung aus & iteriere durch die Ergebnismenge Statement stmt = con.createstatement(); ResultSet rs = stmt.executequery("select Name FROM Kunde"); // Iteriere durch die Ergebnismenge while (rs.next()) System.out.println("Kundenname : " + rs.getstring(1)); // Schliessen aller Resourcen rs.close(); stmt.close(); con.close(); 157
Treiberregistrierung Treiber laden (2 Varianten) Class.forName("com.company.DBDriver"); DriverManager.registerDriver(new com.company.dbdriver()); Oracle oracle.jdbc.driver.oracledriver DB2 COM.ibm.db2.jdbc.app.DB2Driver 158
Verbindungsaufbau Verbindung herstellen String url = "jdbc:subprotocol:datasource"; String user = "scott"; String passwd = "tiger"; Connection con = DriverManager.getConnection(url, user, passwd); JDBC-URL spezifiziert Datenquelle/Datenbank Verbindungsmechanismus (Protokoll, Server-Host und Port) 159
Anweisungsobjekte Statement Basisklasse für alle Anweisungen Verarbeitung einfacher SQL-Anweisungen ohne Parameter PreparedStatement Kapselung einer vorkompilierten Anweisung parametrisierbar Verwendung: wiederholte Ausführung einer Anweisung (mit verschiedenen Parametern) CallableStatement Aufruf von benutzerdefinierten Prozeduren 160
Ausführung einer DB-Anfrage bzw. DB-Änderung Anweisungsobjekt (Statement) erzeugen Statement stmt = con.createstatement(); Anweisung ausführen String querystmt = "SELECT Name, Anschrift FROM Kunde"; ResultSet rset = stmt.executequery(querystmt); Klasse java.sql.statement Ausführung von Anfragen (SELECT) mit executequery Ausführung von Änderungen (DELETE, INSERT, UPDATE) mit executeupdate (liefert Anzahl der betroffenen Tupel/Objekte) String delstmt = "DELETE Kunde WHERE KNr = 1311"; int rows = stmt.executeupdate(delstmt); 161
Ergebnisverarbeitung Navigation über Ergebnismenge (Cursor-Prinzip) while (rset.next()) { // Verarbeitung der einzelnen Tupel... } Zugriff auf Spaltenwerte über getxxx-methoden über Spaltenindex String name = rset.getstring(1); über Spaltenname String name = rset.getstring("name"); 162
Typabbildung von SQL nach Java Typabbildung von Java nach SQL SQL get-methode Java-Typ Java-Typ set-methode SQL SMALLINT INTEGER BIGINT NUMERIC DECIMAL FLOAT REAL DOUBLE PRECISION CHAR VARCHAR BIT VARYING BIT DATE TIME TIMESTAMP getshort getint getlong getbigdecimal getbigdecimal getdouble getfloat getdouble getstring getstring getboolean getbytes getdate gettime gettimestamp short int long BigDecimal BigDecimal double float double String String boolean byte[] Date Time Timestamp byte short int long BigDecimal float double String boolean byte[] Date Time Timestamp setbyte setshort setint setlong setbigdecimal setfloat setdouble setstring setboolean setbytes setdate settime settimestamp SMALLINT SMALLINT INTEGER BIGINT NUMERIC REAL DOUBLE VARCHAR BIT VARYING BIT DATE TIME TIMESTAMP 163
Fehlerbehandlung Fehlerbehandlung mittels Exception-Mechanismus SQLException für alle SQL- und DBMS-Fehler try { // Aufruf von JDBC-Methoden... } catch (SQLException exc) { System.out.println("SQLException: " + exc.getmessage()); } 164
Transaktionssteuerung Methoden von Connection commit() rollback() Auto-Commit Commit-Modus implizites Commit nach jeder Anweisung Transaktion besteht nur aus einer Anweisung Umschalten mittels setautocommit(boolean) Transaktionsisolationsebenen setzen mittels settransactionlevel(int) try { // AutoCommit-Modus ausschalten con.setautocommit(false); // DML-Anweisungen... // COMMIT ausführen con.commit(); } catch (SQLException exc) { // Änderungen zurücknehmen con.rollback(); } TRANSACTION_NONE TRANSACTION_READ_UNCOMMITTED TRANSACTION_READ_COMMITTED TRANSACTION_READ_SERILIZABLE 165
Parametrisierte Anweisungen java.sql sql.preparedstatement Beschleunigung der Ausführung von Anweisungen durch Vorübersetzung und wiederholten Aufruf mit verschiedenen Parametersätzen Anweisungsobjekt (PreparedStatement( PreparedStatement) ) erzeugen String insstmt = "INSERT INTO Kunde VALUES (?,?)"; PreparedStatement stmt = con.preparestatement(insstmt); Anweisungsparameter durch "?" markiert Belegung der Parameter mittels setxxx-methoden stmt.setstring(1, "George"); stmt.setfloat(2, 4500.00); Anweisung ausführen stmt.executeupdate(insstmt); 166
Aufruf von gespeicherten Prozeduren java.sql sql.callablestatement Aufruf von gespeicherten Prozeduren (Stored Procedures) Anweisungsobjekt (CallableStatement( CallableStatement) ) erzeugen und Parameter setzen String callstmt = "{CALL TestProc(?,?)}"; CallableStatement stmt = con.preparecall(callstmt); stmt.setint(1, 42); Registrierung des SQL-Typs der OUT-Parameter & Auslesen der Ergebniswerte mit getxxx-methoden stmt.registeroutparameter(2, java.sql.types.float); stmt.executeupdate(); double res = stmt.getdouble(2); 167
JDBC-Interfaces ResultSet executequery executeupdate Statement DriverManager executequery executeupdate createstatement getconnection getxxx PreparedStatement preparestatement Connection setxxx preparecall Data Types getxxx CallableStatement 168
Neue Features in JDBC 2 Core API - Bestandteil von Java 2 Scrollbare und änderbare ResultSets Batch-Updates SQL-99-Datentypen Benutzerdefinierte Typabbildung Optional API Nutzung von Namensdiensten (JNDI) Connection Pooling Verteilte Transationen RowSets // ARRAY public Array ResultSet.getArray() public void PreparedStatement.setArray() public String Array.getBaseTypeName() public Object Array.getArray() // STRUCT public Object ResultSet.getObject() public void PreparedStatement.setObject() public Object[] Struct.getAttributes() public String Struct.getSQLTypeName() // REF public Ref ResultSet.getRef() public void PreparedStatement.setRef() public String Ref.getBaseTypeName() // OBJECT public Object ResultSet.getObject() public void PreparedStatement.setObject() public String Object.toString() public Object Object.clone() 169
Scrollbare und änderbare Ergebnismengen ResultSet-Typen: TYPE_FORWARD_ONLY (vorwärtsscrollbar & nicht änderbar) TYPE_SCROLL_INSENSITIVE (vorwärts-/rüchwärtsscrollbar & nicht änderbar) TYPE_SCROLL_SENSITIVE (vorwärts-/rüchwärtsscrollbar & änderbar) Concurrency-Typen (CONCUR_READ_ONLY, CONCUR_UPDATABLE) Statement stmt = con.createstatement(resultsettyp, ConcurrencyTyp); ResultSet rs = stmt.executequery(querystring); Ändern im ResultSet: updatexxx(spalte, Wert) Ändern in der DB: updaterow() Löschen aus dem ResultSet und der DB: deleterow() Einfügen ins ResultSet: movetoinsertrow()+ updatexxx(spalte, Wert) Einfügen in die DB: insertrow() 170
JDBC - Beispiel (änderbare Ergebnismengen) import java.sql.*; import java.util.*; class KundenKredit { public static void main (String args []) throws SQLException { // Lade Oracle JDBC Treiber und stelle Verbindung zur Datenbank ORDB her // DriverManager.registerDriver(new // Erzeuge und und führe führe SQL-Anweisung oracle.jdbc.driver.oracledriver()); aus aus Statement Connection stmt con stmt = = DriverManager.getConnection("jdbc:oracle:oci8:@", con.createstatement(resultset.type_scroll_sensitive, createstatement(resultset.type_scroll_sensitive, "scott", "tiger"); // // // Iteriere Iteriere Erzeuge durch durch und die die führe Ergebnismenge SQL-Anweisung aus ResultSet.CONCUR_UPDATABLE); & iteriere durch die Ergebnismenge while while (rs.next()) { ResultSet Statement (rs ()) { rs rs = stmt = stmt.executequery("select con.createstatement(resultset.type_scroll_sensitive, Name, Name, Kreidit KreiditFROM Kunde"); System.out.println("Kundenname : :" " + ResultSet.CONCUR_UPDATABLE); rs.getstring(1)); System.out.println("Bisheriger ResultSet rs = stmt.executequery("select Kreditrahmen: Name, " " + rs.getfloat(2)); Kredit FROM Kunde"); rs.updatefloat(2, while (rs.next()) { rs.getfloat(2) + 100.0f); // // Attribut des des aktuellen Tupels Tupelsändern rs.updaterow(); System.out.println("Kundenname // // Änderung : " + rs.getstring(1)); in die die Datenbank übernehmen }} System.out.println("Bisheriger Kreditrahmen: " + rs.getfloat(2)); rs.updatefloat(2, rs.getfloat(2) + 100.0f); // Attribut des aktuellen Tupels ändern rs.updaterow(); // Änderung in die Datenbank übernehmen } rs.close(); stmt.close(); con.close(); // Schliessen aller Resourcen } } 171
ResultSet-Methoden zum Scrollen Methoden ResultSet.beforeFirst(); ResultSet.first(); ResultSet.beforeFirst(); ResultSet.next(); ResultSet.previous(); ResultSet.absolute(p); ResultSet.relative(p); ResultSet.moveToCurrentRow(); Cursor-Position Vor dem ersten Tupel Erstes Tupel Nach dem letzten Tupel Nächstes Tupel Vorheriges Tupel Tupels auf Indexposition p Tupels auf Indexposition (aktuelle Position + p) Aktuelles Tupel 172
Batch-Updates // Autocommit ausschalten con.setautocommit(false); Statement stmt = con.createstatement(); // Batch-Aufträge sammeln stmt.addbatch(updatestring1);... stmt.addbatch(updatestringn); // Batch-Auftrag ausführen int[] n = stmt.executebatch(); // Autocommit ausschalten con.setautocommit(false); PreparedStatement stmt = con.createstatement(updatestring); // Parameter setzen stmt.setxxx(spalte, Wert); // Auftrag dem Batch hinzufügen stmt.addbatch();... // Parameter setzen stmt.setxxx(spalte, Wert); // Auftrag dem Batch hinzufügen stmt.addbatch(); // Batch-Auftrag ausführen int[] n = stmt.executebatch(); 173