11. JDBC

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
Datenbanken
Prof. Dr.
Stephan Kleuker
270
Überblick: Datenbankanfragen mit JDBC
class DriverManager
Datenbankverbindung Connection con=
DriverManager.getConnection(...);
herstellen
Statement stmt=
con.createStatement();
Datenbankanfrage
Ergebnisse
verarbeiten
Verbindung zur DB
schließen
Datenbanken
ResultSet rs =
stmt.executeQuery(...);
rs.next();
int n = rs.getInt("KNr");
con.close();
Prof. Dr.
Stephan Kleuker
271
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
Datenbanken
Prof. Dr.
Stephan Kleuker
272
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:"
+ "@oracle-srv.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");
Datenbanken
Prof. Dr.
Stephan Kleuker
273
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
Datenbanken
Prof. Dr.
Stephan Kleuker
274
Verbindungsanalyse durch Metadaten (Ausschnitt)
DatabaseMetaData dbmd = con.getMetaData();
System.out.println("DB-Name: " + dbmd.getDatabaseProductName()
+ "\nDB-Version: " + dbmd.getDatabaseMajorVersion()
+ "\nDB-Release: " + dbmd.getDriverMinorVersion()
+ "\nTransaktionen erlaubt: " + dbmd.supportsTransactions()
+ "\nbeachtet GroßKlein :" + dbmd.storesMixedCaseIdentifiers()
+ "\nunterstützt UNION :" + dbmd.supportsUnion()
+ "\nmax. Prozedurname: " + dbmd.getMaxProcedureNameLength());
DB-Name: Oracle
DB-Version: 11
DB-Release: 2
Transaktionen erlaubt: true
beachtet GroßKlein :false
unterstützt UNION :true
max. Prozedurname: 30
Datenbanken
Prof. Dr.
Stephan Kleuker
275
Statement / Anfrage ausführen
• 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
...");
Datenbanken
Prof. Dr.
Stephan Kleuker
276
Metadaten des Anfrageergebnisses
ResultSet rs = stmt.executeQuery("SELECT * FROM Continent");
ResultSetMetaData rsmd = rs.getMetaData();
int spalten = rsmd.getColumnCount();
for (int i = 1; i <= spalten; i++) { // nicht i=0
System.out.println(i + ". Name: " + rsmd.getColumnName(i)
+ " Typ: " + rsmd.getColumnTypeName(i)
+ " Javatyp: " + rsmd.getColumnClassName(i));
}
1. Name: NAME Typ: VARCHAR2 Javatyp: java.lang.String
2. Name: AREA Typ: NUMBER Javatyp: java.math.BigDecimal
Datenbanken
Prof. Dr.
Stephan Kleuker
277
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
• ab 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
Datenbanken
Prof. Dr.
Stephan Kleuker
278
Attributwerte auslesen (1/2)
• Spaltenwerte (Attribute) einer Zeile mit getXXX()-Methoden
lesen
• Treiber konvertiert Daten, falls möglich, deshalb (fast)
immer getString() nutzbar
• 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
Datenbanken
Prof. Dr.
Stephan Kleuker
279
Attributwerte auslesen (2/2)
• Methode getObject()
– Liest jeden beliebigen SQL-Datentyp
– Liefert Ergebnis als entsprechenden Java-Typ
• Nullwerte
– Spalte kann leere Zellen enthalten (Nullwert, SQL-NULL)
– Bei leerer Zelle liefert getInt() ebenfalls das Ergebnis 0
– Unterscheidung zu echter 0 möglich durch wasNull()
• true, falls zuletzt mit getXXX() gelesene Zelle SQLNULL enthielt
• false sonst
Datenbanken
Prof. Dr.
Stephan Kleuker
280
Beispiel: Ausgabe eines Anfrageergebnisses
ResultSet rs = stmt.executeQuery("SELECT * FROM Continent");
ResultSetMetaData rsmd = rs.getMetaData();
int spalten = rsmd.getColumnCount();
while (rs.next()) {
for (int i = 1; i <= spalten; i++) {
System.out.print(rs.getString(i) + " ");
}
System.out.print("\n");
Europe 9562488
}
Asia 45095292
Australia/Oceania 8503474
Africa 30254708
America 39872000
Datenbanken
Prof. Dr.
Stephan Kleuker
281
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
Datenbanken
Prof. Dr.
Stephan Kleuker
282
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
Datenbanken
Prof. Dr.
Stephan Kleuker
283
ResultSet: positionieren (2/3)
• boolean absolute(int pos)
Positioniert ausgehend vom Anfang (pos positiv) oder vom
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
Datenbanken
Prof. Dr.
Stephan Kleuker
284
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
Datenbanken
Prof. Dr.
Stephan Kleuker
285
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]
Datenbanken
Prof. Dr.
Stephan Kleuker
286
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
Datenbanken
Prof. Dr.
Stephan Kleuker
287
Daten ändern
• ResultSet executeQuery(String) führt SELECT-
Anweisung aus
• int 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
Datenbanken
Prof. Dr.
Stephan Kleuker
288
PreparedStatement (1/2)
• PreparedStatement Objekt enthält vorübersetzte SQLBefehle
• 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 > ?");
Datenbanken
Prof. Dr.
Stephan Kleuker
289
PreparedStatement (2/2)
1.
Variablen-Werte übergeben
pstmt.setXXX(index,value);
2.
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();
Datenbanken
Prof. Dr.
Stephan Kleuker
290
COMMIT
• Für Connection con ist automatisch ein „Auto-COMMIT“
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
Datenbanken
Prof. Dr.
Stephan Kleuker
291
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:@oracle-srv.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) {...
}
Datenbanken
Prof. Dr.
Stephan Kleuker
292
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();
}
}
Datenbanken
Prof. Dr.
Stephan Kleuker
293
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 in sqldeveloper\jdbc\lib
Datenbanken
Prof. Dr.
Stephan Kleuker
294
Einbindung von Oracle-JDBC in NetBeans
• Rechtsklick im Projekt auf
Libraries -> Add JAR/Folder
• manövrieren zum Verzeichnis
mit JDBC-Treiber (sinnvoll
Unterordner lib des Projekts)
• markieren und „Öffnen“
Datenbanken
Prof. Dr.
Stephan Kleuker
295
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;
Datenbanken
Prof. Dr.
Stephan Kleuker
296
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);
Datenbanken
Prof. Dr.
Stephan Kleuker
297
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;
}
Datenbanken
Prof. Dr.
Stephan Kleuker
298
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! */
Datenbanken
Prof. Dr.
Stephan Kleuker
299
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:@oracle-srv.edvsz.hs-osna"
+ "brueck.de:1521:Ora11", nutzer, passwort);
//Vorbereitung der PL/SQL-Nutzung
ResultSet rset = null;
CallableStatement cstmt = conn.
prepareCall("{call staedtein(?,?)}");
cstmt.registerOutParameter(2,
OracleTypes.CURSOR);
Datenbanken
Prof. Dr.
Stephan Kleuker
300
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
}
}
}
Datenbanken
Prof. Dr.
Stephan Kleuker
301
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
Datenbanken
Prof. Dr.
Stephan Kleuker
302
Herunterladen