11. JDBC • Grundsätzliche Nutzung von JDBC • Verbindungsaufbau

Werbung
11. JDBC
•
•
•
•
•
Grundsätzliche Nutzung von JDBC
Verbindungsaufbau
Anfragen
Analyse des erhaltenen Ergebnisses
Veränderungen des Ergebnisses
Ziel: Verständnis für Konzepte von JDBC aufbauen
Datenbanksysteme
Prof. Dr. Stephan Kleuker
253
Überblick: Datenbankanfragen mit JDBC
Datenbankverbindung
herstellen
Datenbankanfrage
Ergebnisse
verarbeiten
Verbindung zur DB
schließen
Datenbanksysteme
class DriverManager
Connection con=
DriverManager.getConnection(...);
Statement stmt=
con.createStatement();
ResultSet rs =
stmt.executeQuery(...);
rs.next();
int n = rs.getInt("KNr");
con.close();
Prof. Dr. Stephan Kleuker
254
Verbindungsaufbau mit einer Datenbank
• Laden des Datenbanktreibers
DriverManager.registerDriver(
new oracle.jdbc.driver.OracleDriver());
• Aufbau einer Verbindung
Connection con =
DriverManager.getConnection
("jdbc:postgresql://rechner:5712/eshop",
"user", "password");
• JDBC-URL identifiziert Datenbank
Aufbau: jdbc:subprotocol:subname
• Treibermanager übergibt JDBC-URL der Reihe nach
an registrierte Treiber
• Driver.acceptsURL(String url)
liefert true, falls der Treiber den String akzeptiert
Datenbanksysteme
Prof. Dr. Stephan Kleuker
255
Auslesen der Oracle-Verbindungsdaten
• Oracle „versteckt“ irgendwo Verbindungsdaten in tnsnames.ora
• benötigt wird IP-Adresse, Port (default 1521), Name der DBInstanz (z. B. in SID) + Nutzername und Passwort
• Daraus abgeleiteter Connection-String für HS-DB (SID: Ora11)
Connection con = DriverManager.getConnection(
"jdbc:oracle:thin:@srv20.edvsz.hs-osnabrueck.de"
+":1521:Ora11", "username", "passwort");
• Connection-String für DB auf lokalem Rechner (Oracle XE)
Connection con = DriverManager.
getConnection("jdbc:oracle:thin:@localhost:1521:xe",
"username", "passwort");
Datenbanksysteme
Prof. Dr. Stephan Kleuker
256
Connection
• Ergebnis von DriverManager.getConnection() ist eine
Connection con (oder eine SQLException)
• Connection ist Verbindung zur Datenbank
• Connection ist teure Ressource:
– Aufbau der Verbindung kostet viel Zeit
– Anzahl gleichzeitiger Verbindungen häufig
beschränkt
• Wichtigste Aufgaben:
– Erzeugen von Statement-Objekten
– Beschreibungen von Datenbank und DBMS
erfragen
– Transaktionen handhaben
Datenbanksysteme
Prof. Dr. Stephan Kleuker
257
Statement
• Statement stmt = con.createStatement();
• Wird immer aus bestehender Connection erzeugt
• Aufgabe: Ausführen einer SQL-Anweisung über die
Connection
• Mehrere parallele Statements pro Connection
möglich
• SELECT-Anweisung ausführen:
ResultSet rs =
stmt.executeQuery("SELECT * FROM Kunde");
• Daten ändern:
int updates =
stmt.executeUpdate("DELETE FROM Kunde
...");
Datenbanksysteme
Prof. Dr. Stephan Kleuker
258
Analyse von ResultSet
ResultSet rs =
stmt.executeQuery("SELECT * FROM Kunde");
• Ergebnis einer Selektionsanweisung: Tabelle
• ResultSet enthält einen Datensatz-Zeiger (Cursor)
zum Durchlauf der Tabelle
• Voreinstellung: sequenziell und lesend
• JDBC 2: nichtsequenzielle und aktualisierbare
ResultSets
• Zeiger steht initial vor der ersten Tabellenzeile.
• rs.next() positioniert zur nächsten Zeile, liefert
false, falls bereits auf letzter Zeile
Datenbanksysteme
Prof. Dr. Stephan Kleuker
259
Attributwerte auslesen (1/2)
• Spaltenwerte (Attribute) einer Zeile mit getXXX()Methoden lesen
• Treiber konvertiert Daten, falls möglich
• Beispiel: Lesen einer ganzen Zahl in DB-Spalte
kundenNr:
int n = rs.getInt("kundenNr");
• Effizientere Methode, falls Spaltenindex bekannt:
int n = rs.getInt(4);
• Spaltenindex zum Spaltennamen finden
int findColumn(String columnName)
• Strategie: Spaltenindex einmalig ermitteln und
merken, Werte danach immer über den Index
abrufen
Datenbanksysteme
Prof. Dr. Stephan Kleuker
260
Attributwerte auslesen (2/2)
• Methode getObject()
– Liest jeden beliebigen SQL-Datentyp
– Liefert Ergebnis als entsprechenden Java-Typ
• Nullwerte
– Spalte kann leere Zellen enthalten (Nullwert, SQLNULL)
– Bei leerer Zelle liefert getInt() ebenfalls das
Ergebnis 0.
– Unterscheidung zu echter 0 möglich durch
wasNull()
• true, falls zuletzt mit getXXX() gelesene Zelle
SQL-NULL enthielt
• false sonst
Datenbanksysteme
Prof. Dr. Stephan Kleuker
261
Daten ändern
• ResultSet executeQuery(String) führt SELECTAnweisung aus
• int Statement.executeUpdate(String) führt
INSERT-, UPDATE- oder DELETE-Anweisung aus
– Liefert Anzahl betroffener Zeilen (Update count)
– Auch für sonstige Befehle (CREATE ...) geeignet
• boolean execute(String) führt beliebige SQLAnweisung aus.
– Liefert true, falls Ergebnis ein ResultSet ist, dann
mit getResultSet() Ergebnis abrufbar
– Liefert false, falls Ergebnis ein Update count ist
Datenbanksysteme
Prof. Dr. Stephan Kleuker
262
ResultSet: positionieren und ändern
• Statement createStatement(int
resultSetType, int resultSetConcurrency)
• Parameter (ResultSet-Konstanten) ermöglichen
beliebiges Positionieren (Scrolling) und Ändern der
Datensätze.
• TYPE_FORWARD_ONLY: sequentieller Durchlauf
• TYPE_SCROLL_INSENSITIVE: positionierbar,
Änderungen an Datenbank werden nicht bemerkt.
• TYPE_SCROLL_SENSITIVE: positionierbar,
Änderungen an Datenbank werden bemerkt.
• CONCUR_READ_ONLY: nur lesen
• CONCUR_UPDATABLE: Änderungen möglich
Datenbanksysteme
Prof. Dr. Stephan Kleuker
263
ResultSet: positionieren (1/3)
• void beforeFirst()
Positioniert vor den ersten Satz.
• boolean first()
Positioniert auf den ersten Satz.
• boolean last()
Positioniert auf den letzten Satz.
• void afterLast()
Positioniert hinter den letzten Satz.
Datenbanksysteme
Prof. Dr. Stephan Kleuker
264
ResultSet: positionieren (2/3)
• boolean absolute(int pos)
Positioniert relativ zum Anfang (pos positiv) oder
relativ zum Ende (pos negativ).
• boolean relative(int rows)
• Positioniert relativ zum aktuellen Satz vorwärts
(rows positiv) oder rückwärts (rows negativ).
• boolean next()
Positioniert auf den nächsten Satz.
• boolean previous()
Positioniert auf den vorigen Satz.
Datenbanksysteme
Prof. Dr. Stephan Kleuker
265
ResultSet: positionieren (3/3)
• int getRow()
Liefert aktuelle Satznummer
• boolean isBeforeFirst()
Liefert true, falls vor dem ersten Satz.
• boolean isFirst()
Liefert true, falls auf dem ersten Satz.
• boolean isLast()
Liefert true, falls auf dem letzten Satz.
• boolean isAfterLast()
Liefert true, falls hinter dem letzten Satz.
Datenbanksysteme
Prof. Dr. Stephan Kleuker
266
ResultSet: Datensatz ändern
• Methoden updateXXX() ändern Werte in aktueller Zeile.
rs.absolute(5);
rs.updateString("Name", "Heinz");
rs.updateInt(2, 42);
rs.updateNull(3);
rs.updateRow();
• void updateRow()
Schreibt geänderte Zeile in die Datenbank (*)
• void cancelRowUpdates()
Macht Änderungen rückgängig – nur vor updateRow().
• void deleteRow()
Löscht aktuelle Zeile aus ResultSet
(*) Ob Bearbeitung des ResultSet sich direkt auf die Datenbank
auswirkt, hängt von Autocommit-Einstellung ab [später]
Datenbanksysteme
Prof. Dr. Stephan Kleuker
267
ResultSet: Datensatz einfügen
• Einfügezeile: im ResultSet, nicht in der Datenbank
rs.moveToInsertRow();
rs.updateString("Name", "Callaghan");
rs.updateInt(2, 42);
rs.insertRow();
rs.moveToCurrentRow();
• void moveToInsertRow()
Positioniert auf die Einfügezeile
• void insertRow()
Schreibt Einfügezeile in ResultSet und Datenbank (abhängig
von Autocommit-Einstellung)
• void moveToCurrentRow()
Positioniert von der Einfügezeile auf die aktuelle Zeile im
ResultSet
Datenbanksysteme
Prof. Dr. Stephan Kleuker
268
PreparedStatement (1/2)
• PreparedStatement Objekt enthält vorübersetzte
SQL-Befehle
• geeignet, wenn Statement mehr als einmal
ausgeführt werden muss
• PreparedStatement kann Variablen enthalten, die
jedesmal bei Ausführung definiert werden
• Ansatz: Erzeuge PreparedStatement, identifiziere
Variablen mit ?
PreparedStatement pstmt =
con.prepareStatement ("UPDATE Kunde
SET Status = ? where Umsatz > ?");
Datenbanksysteme
Prof. Dr. Stephan Kleuker
269
PreparedStatement (2/2)
1.
2.
Variablen-Werte übergeben
pstmt.setXXX(index,value);
Statement ausführen
pstmt.executeQuery();
pstmt.executeUpdate();
int goldenerKunde=42000;
PreparedStatement pstmt =
con.prepareStatement ("UPDATE Kunde
SET Status = ? where Umsatz > ?");
pstmt.setString (1, "Gold");
pstmt.setInt (2, goldenerKunde);
pstmt.executeUpdate();
Datenbanksysteme
Prof. Dr. Stephan Kleuker
270
COMMIT
• Für Connection con ist automatisch ein „AutoCOMMIT“ eingestellt, alle Aktionen direkt auf der
Datenbank
• Mit con.setAutoCommit(boolean) einstellbar
• Starteinstellung über con.getAutoCommit()
ermitteln (bei Oracle default: true !)
• con.commit() zum erfolgreichen Abschließen
• con.rollback() zum Verwerfen der Änderungen
seit dem letzten Commit
Datenbanksysteme
Prof. Dr. Stephan Kleuker
271
Verbindungsaufbau (Ausschnitt)
import oracle.jdbc.*; // besser ausschreiben
import java.sql.*;
...
try {
// Oracle JDBC-Treiber laden
DriverManager.
registerDriver(new oracle.jdbc.driver.OracleDriver());
Connection con = DriverManager.
getConnection("jdbc:oracle:thin:@srv20.edvsz"
+".hs-osnabrueck.de:1521:Ora11",
"kleuker", "passwort");
// Oracle DatabaseMetaData fuer Meta-Informationen
DatabaseMetaData meta = con.getMetaData();
// Beispielmetainformation: Treiberversion
System.out.println("JDBC driver version is "+
meta.getDriverVersion()+"\n");
befehl=con.createStatement(); //private Statement befehl
}
catch (SQLException e) {...
} Datenbanksysteme
Prof. Dr. Stephan Kleuker
272
Einfache Verbindungsnutzung (Ausschnitt)
ResultSet result;
ResultSetMetaData metaresult;
try {
result=befehl.executeQuery("SELECT * FROM City");
metaresult=result.getMetaData();
int spalten = metaresult.getColumnCount();
while(result.next()){
for(int i=1; i<=spalten;i++)
ausgabe.append(result.getString(i)+" ");
ausgabe.append("\n");
}
ausgabe.append("Bearbeitung abgeschlossen\n");
result.close();
}
catch (SQLException es) {
while (es!=null){ //evtl. mehr als eine Exception
fehler.append("Fehlercode: "+es.getErrorCode()+"\n");
fehler.append("SQL State: "+es.getSQLState()+"\n");
fehler.append(es+"\n");
es= es.getNextException();
}
}
Datenbanksysteme
Prof. Dr. Stephan Kleuker
273
Einbindung von Oracle-JDBC in Eclipse
Rechtsklick auf Projekt,
dann Properies wählen,
„Java Build Path“, dann
Reiter Libraries, dann
„Add External JARs...“
Quellen: Oracle Web-Seiten
C:\sqldeveloper\jdbc\lib\ojdbc6.jar
Dann passenden Treiber suchen,
z. B. ojdbc6.jar
Datenbanksysteme
Prof. Dr. Stephan Kleuker
274
Erinnerung: PL/SQL-Funktion
CREATE OR REPLACE
FUNCTION anzahlNasen(namePar Angestellte.Name%TYPE)
RETURN INTEGER
/* oder NUMBER */
IS
ergebnis INTEGER;
BEGIN
IF namePar = 'Meier'
THEN
RAISE_APPLICATION_ERROR(-20300,
'Meiers nicht zählbar');
END IF;
SELECT COUNT(*)
INTO ergebnis
FROM Angestellte
WHERE Angestellte.Name=namePar;
RETURN ergebnis;
END;
Datenbanksysteme
Prof. Dr. Stephan Kleuker
275
PL/SQL in Java
• Grundsätzlich besteht bei der Ausführung große
Ähnlichkeit zu PreparedStatements
• benötigt wird Connection-Objekt con
CallableStatement stmt=con.prepareCall(
"{? = call anzahlNasen(?)}");
• man beachte geschweifte Klammern, bei
Prozeduraufruf wird „ ? = “ am Anfang weggelassen
• in Oracle auch möglich:
CallableStatement stmt=con.prepareCall(
"BEGIN ? := anzahlNasen(?); END;");
• Ausgabeparameter müssen registriert werden
stmt.registerOutParameter(1,Types.INTEGER);
Datenbanksysteme
Prof. Dr. Stephan Kleuker
276
Beispiel
public int nasenZaehlen(String nachname){
int ergebnis=0;
try {
CallableStatement stmt=con.prepareCall(
"{? = call anzahlNasen(?)}");
stmt.registerOutParameter(1,Types.INTEGER);
stmt.setString(2,nachname);
stmt.execute();
ergebnis=stmt.getInt(1);
} catch (SQLException e) {
if(e.getErrorCode()==20300) // von RAISE_APPLICATION_ERROR
ergebnis=-1;
else{
ausnahmeAusgeben(e);
ergebnis=Integer.MIN_VALUE;
}
}
return ergebnis;
}
Datenbanksysteme
Prof. Dr. Stephan Kleuker
277
Nutzung von Cursor als Ergebnis (1/3)
import
import
import
import
import
import
java.sql.CallableStatement;
java.sql.Connection;
java.sql.DriverManager;
java.sql.ResultSet;
java.util.Scanner;
oracle.jdbc.OracleTypes;
/* in Oracle fuer das MondialBeispiel
CREATE OR REPLACE PROCEDURE STAEDTEIN(
Land IN VARCHAR,
stadtcursor IN OUT SYS_REFCURSOR) AS
BEGIN
OPEN stadtcursor for
SELECT *
FROM City
WHERE City.Country=Land;
END STAEDTEIN;
*/
/* Hinweis: Cursor nur lesbar, nicht schreibbar! */
Datenbanksysteme
Prof. Dr. Stephan Kleuker
278
Nutzung von Cursor als Ergebnis (2/3)
public class PLSQLCursorNutzungInJava { //
public static void main(String[] s){
String nutzer="kleuker";
String passwort="kleuker";
try {
DriverManager.registerDriver(new
oracle.jdbc.driver.OracleDriver());
//Verbindung aufbauen in der HS zur DB
Connection conn = DriverManager.getConnection
("jdbc:oracle:thin:@srv20.edvsz.hs-osnabrueck"
+".de:1521:Ora11", nutzer, passwort);
//Vorbereitung der PL/SQL-Nutzung
ResultSet rset = null;
CallableStatement cstmt = conn.
prepareCall("{call staedtein(?,?)}");
cstmt.registerOutParameter(2,
OracleTypes.CURSOR);
Datenbanksysteme
Prof. Dr. Stephan Kleuker
279
Nutzung von Cursor als Ergebnis (3/3)
//Testprogramm
String eingabe="";
while(!eingabe.equalsIgnoreCase("Ende")){
System.out.print(
"Laenderkuerzel (Ende mit \"Ende\"): ");
eingabe=(new Scanner(System.in)).next();
cstmt.setString(1,eingabe);
cstmt.execute();
rset = (ResultSet) cstmt.getObject(2);
while(rset.next())
System.out.println("Stadt: "
+rset.getString("name")
+" Einwohner:"
+rset.getInt("population"));
}
conn.close();
}
catch(Exception e){ // ausarbeiten, wie immer
}
}
Datenbanksysteme
}
Prof. Dr. Stephan Kleuker
280
Beispieldialog
Laenderkuerzel (Ende mit "Ende"): GR
Stadt: Piraeus Einwohner:196389
Stadt: Patrai Einwohner:142163
Stadt: Larisa Einwohner:102426
Stadt: Iraklion Einwohner:102398
Stadt: Volos Einwohner:71378
Stadt: Kavalla Einwohner:56705
Stadt: Athens Einwohner:885737
Stadt: Thessaloniki Einwohner:406413
Laenderkuerzel (Ende mit "Ende"): LAR
Stadt: Tripoli Einwohner:0
Stadt: Bengasi Einwohner:0
Laenderkuerzel (Ende mit "Ende"): AND
Stadt: Andorra la Vella Einwohner:15600
Laenderkuerzel (Ende mit "Ende"): enDE
Datenbanksysteme
Prof. Dr. Stephan Kleuker
281
Herunterladen