[+/-]
In diesem Abschnitt wird Grundlagenwissen über JDBC vermittelt.
Wenn Sie JDBC außerhalb eines Anwendungsservers benutzen,
wird die Einrichtung von Verbindungen über die Klasse
DriverManager
verwaltet.
Sie müssen dem DriverManager
mitteilen,
mit welchen JDBC-Treibern er sich verbinden soll. Die
einfachste Möglichkeit besteht darin, die Methode
Class.forName()
auf der Klasse aufzurufen,
welche die java.sql.Driver
-Schnittstelle
implementiert. Bei MySQL Connector/J heißt diese Klasse
com.mysql.jdbc.Driver
. Mit dieser Methode
können Sie eine externe Konfigurationsdatei verwenden, um den
Namen der Treiberklasse und die Treiberparameter zu
übergeben, wenn eine Datenbankverbindung aufgebaut wird.
Der folgende Java-Code zeigt, wie Sie MySQL Connector/J in der
main()
-Methode Ihrer Anwendung registrieren
könnten:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; // Achtung, nicht com.mysql.jdbc.* importieren, // sonst bekommen Sie Probleme! public class LoadDriver { public static void main(String[] args) { try { // Der Aufruf von newInstance() ist ein Workaround // für einige misslungene Java-Implementierungen Class.forName("com.mysql.jdbc.Driver").newInstance(); } catch (Exception ex) { // Fehler behandeln } }
Nachdem der Treiber beim DriverManager
registriert wurde, können Sie eine
Connection
-Instanz zur Verbindung mit einer
bestimmten Datenbank erzeugen, indem Sie
DriverManager.getConnection()
aufrufen:
Beispiel 25.1. Eine Verbindung vom DriverManager
erhalten
Dieses Beispiel zeigt, wie Sie eine
Connection
-Instanz vom
DriverManager
bekommen können. Es gibt
verschiedene Signaturen für die
getConnection()
-Methode. Wie man mit
ihnen umgeht, entnehmen Sie bitte der API-Dokumentation, die
mit Ihrem JDK mitgeliefert wurde.
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; ... try { Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test?user=monty&password=greatsqldb"); // Verbindung benutzen .... } catch (SQLException ex) { // Fehler behandeln System.out.println("SQLException: " + ex.getMessage()); System.out.println("SQLState: " + ex.getSQLState()); System.out.println("VendorError: " + ex.getErrorCode()); }
Sobald eine Connection
eingerichtet
ist, kann sie genutzt werden, um
Statement
- und
PreparedStatement
-Objekte
auszuführen und Metadaten über die Datenbank abzurufen.
Dies wird in den folgenden Abschnitten erklärt.
Mit Statement
-Objekten können Sie
grundlegende SQL-Anfragen ausführen und die Ergebnisse
mithilfe der weiter unten beschriebenen Klasse
ResultSet
abrufen.
Um ein Statement
-Objekt zu erzeugen,
rufen Sie die Methode createStatement()
auf
dem Connection
-Objekt auf, das Sie mit
einer der zuvor beschriebenen Methoden
DriverManager.getConnection()
oder
DataSource.getConnection()
erzeugt haben.
Wenn Sie ein Statement
-Objekt angelegt
haben, können Sie eine SELECT
-Anfrage
ausführen, indem Sie executeQuery(String)
mit der gewünschten SQL-Anweisung aufrufen.
Um Daten in der Datenbank zu aktualisieren, verwenden Sie die
Methode executeUpdate(String SQL)
. Diese
liefert die Anzahl der von dem Update betroffenen Zeilen
zurück.
Wenn Sie im Voraus noch nicht wissen, ob Ihre SQL-Anweisung
ein SELECT
oder ein
UPDATE
/INSERT
wird,
können Sie die Methode execute(String SQL)
verwenden. Sie gibt true zurück, wenn die SQL-Anfrage ein
SELECT
ist, oder false, wenn sie ein
UPDATE
, INSERT
oder
DELETE
ist. Handelt es sich um ein
SELECT
, so können Sie die Ergebnisse mit
der Methode getResultSet()
abrufen. Für
UPDATE
-, INSERT
- oder
DELETE
-Anweisungen können Sie die Anzahl
der betroffenen Zeilen in Erfahrung bringen, indem Sie
getUpdateCount()
auf dem
Statement
-Objekt aufrufen.
Beispiel 25.2. Mit java.sql.Statement eine SELECT
-Anfrage ausführen
// conn sei eine bereits vorhandene JDBC-Verbindung Statement stmt = null; ResultSet rs = null; try { stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT foo FROM bar"); // oder, wenn Sie im Voraus nicht wissen, ob die // Anfrage ein SELECT ist... if (stmt.execute("SELECT foo FROM bar")) { rs = stmt.getResultSet(); } // Tue etwas mit dem ResultSet .... } finally { // Ressourcen sollten immer in einem // finally{}-Block // in der umgekehrten Reihenfolge ihrer Zuweisung // freigegeben werden if (rs != null) { try { rs.close(); } catch (SQLException sqlEx) { // ignore } rs = null; } if (stmt != null) { try { stmt.close(); } catch (SQLException sqlEx) { // ignore } stmt = null; } }
Seit der MySQL-Server-Version 5.0 in Verbindung mit
Connector/J 3.1.1 oder höher ist die Schnittstelle
java.sql.CallableStatement
mit Ausnahme
der Methode getParameterMetaData()
vollständig implementiert.
Mehr über gespeicherte Prozeduren in MySQL erfahren Sie in Kapitel 19, Gespeicherte Prozeduren und Funktionen.
Connector/J stellt Funktionalität für gespeicherte
Prozeduren über die JDBC-Schnittstelle
CallableStatement
zur Verfügung.
Hinweis.
Aktuelle Versionen von MySQL-Server geben nicht genug
Informationen zurück, damit der JDBC-Treiber
Ergebnismengen-Metadaten für Callable Statements liefern
kann. Folglich wird ResultSetMetaData
für CallableStatement
unter Umständen
den Wert NULL
zurückgeben.
Das folgende Beispiel zeigt eine gespeicherte Prozedur, die
den Wert von inOutParam
um 1 inkrementiert
und den im inputParam
übergebenen String
als ResultSet
zurückgibt:
Beispiel 25.3. Gespeicherte Prozeduren
CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), INOUT inOutParam INT) BEGIN DECLARE z INT; SET z = inOutParam + 1; SET inOutParam = z; SELECT inputParam; SELECT CONCAT('zyxw', inputParam); END
Um die Prozedur demoSp
mit Connector/J
einzusetzen, gehen Sie folgendermaßen vor:
Sie bereiten das Callable Statement mit
Connection.prepareCall()
vor.
Beachten Sie, dass Sie die Escape-Syntax von JDBC anwenden müssen, und dass die runden Klammern um die Parameter-Platzhalter obligatorisch sind:
Beispiel 25.4. Using Connection.prepareCall()
import java.sql.CallableStatement; ... // // Bereite Aufruf der gespeicherten Prozedur 'demoSp' // mit zwei Parametern vor // // Beachten Sie die JDBC-Escape-Syntax ({call ...}) // CallableStatement cStmt = conn.prepareCall("{call demoSp(?, ?)}"); cStmt.setString(1, "abcdefg");
Note.
Connection.prepareCall()
ist eine
aufwändige Methode, da der Treiber Metadaten abfragen
muss, um die Ausgabeparameter zu unterstützen. Aus
Performance-Gründen sollten Sie unnötige Aufrufe von
Connection.prepareCall()
vermeiden,
indem Sie
CallableStatement
-Objekte in
Ihrem Code wiederverwenden.
Registrieren Sie die Ausgabeparameter (sofern vorhanden)
Um die Werte der Ausgabeparameter (Parameter, die Sie beim
Anlegen der gespeicherten Prozedur als
OUT
oder INOUT
gekennzeichnet haben) abrufen zu können, müssen sie in
JDBC vor der Ausführung der Anweisung mit einer der
diversen
registerOutputParameter()
-Methoden in
der Klasse CallableStatement
registriert worden sein:
Beispiel 25.5. Registrierung von Ausgabeparametern
import java.sql.Types; ... // // Connector/J unterstützt benannte und indizierte // Ausgabeparameter. Mit beiden Methoden können Sie // Ausgabeparameter registrieren oder, unabhängig // von der gewählten Registrierungsmethode, // auch abrufen. // // Die folgenden Beispiele zeigen, wie die verschiedenen // Registrierungsmethoden für Ausgabeparameter verwendet werden // (wobei Sie natürlich nur eine Registrierung // pro Parameter verwenden). // // // Registriert den zweiten Parameter als Ausgabe und // verwendet den Typ 'INTEGER' für Rückgabewerte von // getObject() // cStmt.registerOutParameter(2, Types.INTEGER); // // Registriert den benannten Parameter 'inOutParam' und // verwendet den Typ 'INTEGER' für Rückgabewerte von // getObject() // cStmt.registerOutParameter("inOutParam", Types.INTEGER); ...
Stellt die Eingabeparameter ein (sofern vorhanden)
Eingabe und Ein/Ausgabeparameter werden ebenso gesetzt,
wie für PreparedStatement
-Objekte.
Allerdings können
CallableStatement
-Parameter auch
namentlich eingestellt werden:
Beispiel 25.6. Einstellung von CallableStatement
-Eingabeparametern
... // // Parameter nach Index einstellen // cStmt.setString(1, "abcdefg"); // // Oder Parameter mit // Namen einstellen // cStmt.setString("inputParameter", "abcdefg"); // // 'in/out'-Parameter nach Index einstellen // cStmt.setInt(2, 1); // // Oder 'in/out'-Parameter // mit Namen einstellen // cStmt.setInt("inOutParam", 1); ...
Führen Sie CallableStatement
aus
und rufen Sie Ergebnismengen oder Ausgabeparameter, sofern
vorhanden, ab.
CallableStatement
unterstützt zwar
alle Ausführungsmethoden von
Statement
(executeUpdate()
,
executeQuery()
oder
execute()
), aber
execute()
ist am flexibelsten, da Sie
mit dieser Methode nicht im Voraus wissen müssen, ob die
gespeicherte Prozedur Ergebnismengen unterstützt:
Beispiel 25.7. Abruf von Ergebnissen und Ausgabeparameterwerten
... boolean hadResults = cStmt.execute(); // // Alle zurückgelieferten Ergebnismengen verarbeiten // while (hadResults) { ResultSet rs = cStmt.getResultSet(); // Ergebnismenge verarbeiten ... hadResults = rs.getMoreResults(); } // // Ausgabeparameter abrufen // // Connector/J unterstützt Abruf sowohl nach Index // als auch nach Namen // int outputValue = rs.getInt(2); // index-based outputValue = rs.getInt("inOutParam"); // name-based ...
Vor der Version 3.0 der JDBC-API gab es kein Standardmittel,
um Schlüsselwerte aus Datenbanken zu holen, die „Auto
Increment“- oder Identitätsspalte unterstützten. Mit
älteren JDBC-Treibern für MySQL konnten Sie immer eine
MySQL-spezifische Methode auf der Schnittstelle
Statement
verwenden, oder
SELECT LAST_INSERT_ID()
sagen, wenn Sie
eine INSERT
-Operation auf einer Tabelle mit
AUTO_INCREMENT
-Schlüssel ausgeführt
hatten. Doch die MySQL-spezifische Methode ist nicht
portierbar und der Abruf der Werte des
AUTO_INCREMENT
-Schlüssels mit
SELECT
macht einen zusätzlichen
Datenbankzugriff erforderlich, ist also nicht die
effizienteste Methode. Die folgenden Codestücke zeigen die
drei unterschiedlichen Möglichkeiten zum Abruf von
AUTO_INCREMENT
-Werten. Zuerst zeigen wir,
wie die neue JDBC-3.0-Methode
getGeneratedKeys()
benutzt wird, die
jetzt die Methode der Wahl ist, wenn Sie
AUTO_INCREMENT
-Schlüssel abfragen müssen
und Zugang zu JDBC-3.0 haben. Das zweite Beispiel zeigt, wie
Sie denselben Wert mit einer standardmäßigen SELECT
LAST_INSERT_ID()
-Anfrage abholen können. Das letzte
Beispiel zeigt, wie aktualisierbare Ergebnismengen den
AUTO_INCREMENT
-Wert abrufen können, wenn
die Methode insertRow()
verwendet wurde.
Beispiel 25.8. AUTO_INCREMENT
-Spaltenwerte mit
Statement.getGeneratedKeys()
abrufen
Statement stmt = null; ResultSet rs = null; try { // // Erzeugt ein Statement-Objekt, das wir für // 'normale' Ergebnismengen verwenden können. // Die 'conn'-Verbindung zu einer MySQL-Datenbank // sei bereits vorhanden. stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_UPDATABLE); // // DDL-Anfragen für dieses Beispiel // stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial"); stmt.executeUpdate( "CREATE TABLE autoIncTutorial (" + "priKey INT NOT NULL AUTO_INCREMENT, " + "dataField VARCHAR(64), PRIMARY KEY (priKey))"); // // Fügt eine Zeile ein, die einen AUTO INCREMENT-Schlüssel im // Feld 'priKey' generiert // stmt.executeUpdate( "INSERT INTO autoIncTutorial (dataField) " + "values ('Can I Get the Auto Increment Field?')", Statement.RETURN_GENERATED_KEYS); // // So kann man den Wert einer Auto-Increment-Spalte mit // Statement.getGeneratedKeys() abholen // int autoIncKeyFromApi = -1; rs = stmt.getGeneratedKeys(); if (rs.next()) { autoIncKeyFromApi = rs.getInt(1); } else { // hier Ausnahme auslösen } rs.close(); rs = null; System.out.println("Key returned from getGeneratedKeys():" + autoIncKeyFromApi); } finally { if (rs != null) { try { rs.close(); } catch (SQLException ex) { // ignorieren } } if (stmt != null) { try { stmt.close(); } catch (SQLException ex) { // ignorieren } } }
Beispiel 25.9. Abruf von AUTO_INCREMENT
-Spaltenwerten mit
SELECT LAST_INSERT_ID()
Statement stmt = null; ResultSet rs = null; try { // // Erzeugt ein Statement-Objekt, das wir für // 'normale' Ergebnismengen benutzen können. stmt = conn.createStatement(); // // DDL-Anfragen für dieses Beispiel // stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial"); stmt.executeUpdate( "CREATE TABLE autoIncTutorial (" + "priKey INT NOT NULL AUTO_INCREMENT, " + "dataField VARCHAR(64), PRIMARY KEY (priKey))"); // // Fügt eine Zeile ein, die einen AUTO INCREMENT-Schlüssel im Feld // 'priKey' generiert // stmt.executeUpdate( "INSERT INTO autoIncTutorial (dataField) " + "values ('Can I Get the Auto Increment Field?')"); // // Verwendet die Funktion MySQL LAST_INSERT_ID(), // um dasselbe wie getGeneratedKeys() zu tun // int autoIncKeyFromFunc = -1; rs = stmt.executeQuery("SELECT LAST_INSERT_ID()"); if (rs.next()) { autoIncKeyFromFunc = rs.getInt(1); } else { // hier Ausnahme auslösen } rs.close(); System.out.println("Key returned from " + "'SELECT LAST_INSERT_ID()': " + autoIncKeyFromFunc); } finally { if (rs != null) { try { rs.close(); } catch (SQLException ex) { // ignorieren } } if (stmt != null) { try { stmt.close(); } catch (SQLException ex) { // ignorieren } } }
Beispiel 25.10. Retrieving AUTO_INCREMENT
column values in
Updatable ResultSets
Statement stmt = null; ResultSet rs = null; try { // // Erzeugt ein Statement-Objekt, das wir für // 'normale' ebenso wie für 'aktualisierbare' Ergebnismengen verwenden können. // Die 'conn''-Verbindung zu // einer MySQL-Datenbank sei bereits vorhanden // stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_UPDATABLE); // // DDL-Anfragen für dieses Beispiel // stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial"); stmt.executeUpdate( "CREATE TABLE autoIncTutorial (" + "priKey INT NOT NULL AUTO_INCREMENT, " + "dataField VARCHAR(64), PRIMARY KEY (priKey))"); // // So wird ein AUTO INCREMENT-Schlüssel von einer // aktualisierbaren Ergebnismenge abgerufen // rs = stmt.executeQuery("SELECT priKey, dataField " + "FROM autoIncTutorial"); rs.moveToInsertRow(); rs.updateString("dataField", "AUTO INCREMENT here?"); rs.insertRow(); // // Der Treiber fügt die Zeilen am Ende hinzu // rs.last(); // // Jetzt müsste die zuvor eingefügte Zeile erreicht sein // int autoIncKeyFromRS = rs.getInt("priKey"); rs.close(); rs = null; System.out.println("Key returned for inserted row: " + autoIncKeyFromRS); } finally { if (rs != null) { try { rs.close(); } catch (SQLException ex) { // ignorieren } } if (stmt != null) { try { stmt.close(); } catch (SQLException ex) { // ignorieren } } }
Wenn Sie den Code des obigen Beispiels ausführen, müsste
folgende Ausgabe erscheinen: Key returned from
getGeneratedKeys()
: 1 Key returned from
SELECT LAST_INSERT_ID()
: 1 Key returned for
inserted row: 2 Bitte denken Sie daran, dass die Verwendung
der Anfrage SELECT LAST_INSERT_ID()
gelegentlich knifflig sein kann, da der Wert dieser Funktion
als Geltungsbereich die Verbindung hat. Wenn also eine andere
Anfrage auf derselben Verbindung ausgeführt wird, wird der
Wert überschrieben. Dagegen hat die Methode
getGeneratedKeys()
das
Statement
-Objekt als Geltungsbereich
und kann daher zwar genutzt werden, wenn andere Anfragen auf
derselben Verbindung ausgeführt werden, aber nicht, wenn die
Anfragen auf dem Statement
-Objekt
ausgeführt werden.
Dies ist eine Übersetzung des MySQL-Referenzhandbuchs, das sich auf dev.mysql.com befindet. Das ursprüngliche Referenzhandbuch ist auf Englisch, und diese Übersetzung ist nicht notwendigerweise so aktuell wie die englische Ausgabe. Das vorliegende deutschsprachige Handbuch behandelt MySQL bis zur Version 5.1.