18.4.4 | Datenquellen |
JDBC-Datenquellensind in ihrer Anwendung flexibler als das Treibermanager-API und könnten die Klasse DriverManager als Standardmechanismus für das Erzeugen von Datenbankverbindungen ablösen. Datenquellen sind seit der J2SDK 1.4 in den Standardklassen enthalten.
Datenquellen verbergen den Mechanismus, über den neue Datenbankverbindungen aufgebaut werden. Nach der einmaligen Initialisierung der Datenquelle kann man über eine Datenquelle beliebig viele Verbindungen zum selben Datenbanksystem aufbauen, ohne die erneute Angabe von Verbindungsparametern. Datenquellen werden z. B. häufig an eine JNDI-Verzeichnishierarchie gebunden und später von Anwendungen über einen logischen Namen abgerufen (siehe Kapitel 17). Dadurch braucht die Anwendung keine Verbindungsinformationen zur Datenbank, sondern lediglich den logischen Namen der Datenquelle im JNDI-Verzeichnis. Oracle8i verwendet z. B. dieses Prinzip:
Für JDBC-Datenquellen gibt es folgende Einsatzgebiete:
- Trennung zwischen dem Setzen der Verbindungsparameter und dem Abrufen der Verbindungen (allgemeine Kapselung des Verbindungsaufbaus).
- Implementierung von Connection Pools.
- Durchführung von globalen bzw. verteilten Transaktionen.
- Loggen von Datenbankverbindungen.
Eine Klasse, die eine JDBC-Datenquelle darstellt, implementiert im einfachsten Fall das Java-Interface DataSource. Dieses Interface definiert Methoden, über die man Verbindungsinformationen setzen und danach Verbindungen in Form von Connection-Exemplaren abrufen kann. Für die Unterstützung wiederverwendbarer Verbindungen und Verbindungen für verteilte Transaktionen wurden die Java-Interfaces PooledConnectionDataSource bzw. XAConnectionDataSource definiert. Die beiden zuletzt genannten werden in der Praxis jedoch weniger häufig verwendet und sind lediglich als Lowlevel-API anzusehen, die für die Implementierung höherwertiger Dienste benutzt werden. Exemplare einer PooledConnectionDataSource kann man z. B. in einem Connection Pool für die Erzeugung von Verbindungen einsetzen (siehe Abschnitt 18.10.3). Von XADataSource wird z. B. in Implementierungen des JTA (Java-Transaction-API) Gebrauch gemacht.
Die einzelnen Typen von JDBC-Datenquellen werden in Abbildung 18.8 gegenübergestellt.
Prinzipiell gibt es zwei Initialisierungs-Alternativen:
- Direkte Erzeugung eines neuen Exemplars der Implementierungsklasse mit dem new-Operator und anschließende Verwendung in der Anwendung. Datenquellen dieser Art stehen nur lokal innerhalb der aktuellen Anwendung zur Verfügung. Diese Variante wird in der Regel nur bei einfachen Datenbankverbindungen benutzt.
- Initialisierung einer Datenquelle und Bindung an einen Kontext eines JNDI-Verzeichnisses. Anschließend kann die Datenquelle über die JNDI-Schnittstelle in einer Anwendung abgerufen und für den Verbindungsaufbau zum Datenbanksystem benutzt werden. In diesem Fall ist die Verbindungsinformation global verfügbar und kann prinzipiell von verschiedenen Clients abgerufen werden,
Für die lokale Initialisierung einer Datenquelle muss man folgende Schritte durchführen:Die Umsetzung der einzelnen Schritte wird im Folgenden am Beispiel einer Datenquelle für einfache Datenbankverbindungen gezeigt.
- Erzeugen eines neuen Exemplars der Datenquelle.
- Setzen der Verbindungsparameter (JDBC-URL, Nutzer, Passwort, ...).
- Aufbauen der Verbindung zur Datenbank.
// Import-Anweisungen zur Nutzung der Datenquelle import java.sql.*; import oracle.jdbc.pool.OracleDataSource; import javax.sql.DataSource; ... // Laden des JDBC-Treibers Class.forName("oracle.jdbc.driver.OracleDriver"); // Erzeugen einer neuen Datenquelle DataSource ds = new OracleDataSource(); // Setzen von Verbindungsparametern ((OracleDataSource)ds).setUser("shop"); ((OracleDataSource)ds).setPassword("shop"); ((OracleDataSource)ds).setDriverType("thin"); ((OracleDataSource)ds).setPortNumber(1521); ((OracleDataSource)ds).setServerName("localhost"); ((OracleDataSource)ds).setDatabaseName("orcl"); // Aufbauen einer neuen Verbindung Connection con = ds.getConnection(); // Hier Ausführen von SQL-Anweisungen ... // Schließen der Verbindung con.close(); ...Im Beispiel wird die neue Datenquelle in einer Variable vom Typ DataSource gespeichert. Da die Methoden zum Setzen der Verbindungsinformation Oracle-spezifisch sind, muss beim Aufruf ein Cast in OracleDataSource durchgeführt werden. Im Beispiel werden die Parameter jeweils einzeln gesetzt. Prinzipiell ist es auch möglich, alle Verbindungsparameter gemeinsam über die Methode setURL() zu setzen:String url = "jdbc:oracle:thin:shop/shop@localhost:1521:orcl"; ((OracleDataSource)ds).setURL(url);Auf diese Weise kann man sich einige Zeilen Quellcode sparen.
In der Client-Anwendung initialisierte JDBC-Datenquellen können im Programmcode auch an das serverseitige JNDI-Verzeichnis gebunden werden, jedoch nur, wenn die Bindung serverintern ausgeführt wird. Hierzu muss man aus dem JNDI-Verzeichnis über eine Lookup-Operation den entsprechenden Kontext aufrufen und über die Methode bind() die neu erzeugte Datenquelle unter einem Namen an den Kontext binden:DataSource ds; // Datenquelle anlegen und intialisieren ... InitialContext ic = new InitialContext(); // Server-seitiges Lookup des Kontextes Context ctx = (Context)ic.lookup("/jdbc"); // Binden der Datenquelle in den Kontext ctx.bind("shopds", ds); ...
Sowohl Datenquellen als auch das Treibermanager-API verfügen über eine integrierte Protokollfunktionalität. Protokolliert werden treiberinterne Aktionen, die für die Fehlersuche in der eigenen Anwendung hilfreich sein könnten. Die Protokollierung ist per Voreinstellung deaktiviert und muss explizit über die Methode setLogWriter() aktiviert werden. Folgendes Beispiel zeigt, wie das Protokoll des JDBC-Treibers über eine JDBC-Datenquelle in die Datei test.log geschrieben wird:FileWriter logfile = new FileWriter("test.log"); DataSource ds; // Hier Initialisierung der Datenquelle ... // Setzen des Protokoll-Streams ds.setLogWriter(new PrintWriter(logfile)); ...Die Aktivierung der Protokollierung über das Treibermanager-API funktioniert entsprechend. Im folgenden Beispiel wird das Treiberprotokoll des Treibermanagers auf der Standard-Ausgabe ausgegeben:PrintWriter logger; logger = new PrintWriter(new OutputStreamWriter(System.out)); DriverManager.setLogWriter(logger); // Jetzt Aufbau der Verbindung undDie Aktivierung der Protokollierung muss nicht unbedingt zu Beginn erfolgen. Sie kann an beliebiger Stelle initiiert werden und wirkt sich auf alle folgenden Operationen aus.
Ein Beispiel für ein Protkoll des Oracle-Treibers ist in Abbildung 18.9 dargestellt.Es wurde vom folgenden Code erzeugt:
DataSource ds; ... Connection con = ds.getConnection(); ds.setLogWriter(new PrintWriter(new OutputStreamWriter(System.out))); Statement stmt = con.createStatement(); String sql = "INSERT INTO autoren values('Singer', 'Reiner', 3, 1, 11)"; stmt.executeUpdate(sql); prep.close(); con.close(); ...Die Protokollierung kann zu jeder Zeit wieder deaktiviert werden, indem man setLogWriter() mit dem Wert null als Parameter aufruft:ds.setLogWriter(null);