W3docs

Java JDBC-Verbindung

Datenbankverbindungen in Java mit dem Connection-Interface öffnen, schließen und konfigurieren.

Eine Connection ist eine aktive Sitzung mit der Datenbank. Es ist das Objekt, das Sie von DriverManager (oder einer DataSource) zurückbekommen, und es ist die Fabrik für alles andere — Statements, Transaktionen, Savepoints, Metadaten. Eine Verbindung ist auch eine knappe, teure Ressource: Jede offene Verbindung belegt einen Socket und eine serverseitige Sitzung, daher lautet die Grundregel: spät öffnen, schnell schließen.

Dieses Kapitel behandelt, wie man eine Verbindungs-URL aufbaut, die drei Möglichkeiten eine Connection zu öffnen, warum try-with-resources unverzichtbar ist, die Sitzungseinstellungen, die man anpassen kann, und die Fehler, die bei Fehlkonfigurationen auftreten. Es wird vorausgesetzt, dass Sie bereits einen Treiber geladen haben — siehe JDBC-Treiber und die JDBC-Einführung für das Gesamtbild.

Die Verbindungs-URL

Alles, was DriverManager braucht, um eine Datenbank zu finden und zu erreichen, ist in der URL kodiert:

jdbc:<subprotocol>://<host>:<port>/<database>?<key=value&...>

Zum Beispiel jdbc:postgresql://db.internal:5432/shop?ssl=true. Das Präfix jdbc: ist obligatorisch; das Subprotokoll wählt den Treiber; der Rest ist herstellerspezifisch, aber konventionell bestehend aus Host, Port, Datenbank und einem Query-String mit Einstellungsoptionen. Ein paar reale URLs zum Erkennen:

DatenbankBeispiel-URL
PostgreSQLjdbc:postgresql://localhost:5432/shop
MySQLjdbc:mysql://localhost:3306/shop?useSSL=true
H2 (In-Memory)jdbc:h2:mem:testdb
SQLite (Datei)jdbc:sqlite:/data/shop.db

Das Subprotokoll (postgresql, mysql, h2, sqlite) legt fest, welcher registrierte Treiber die URL verarbeiten soll — deshalb teilt JDBC dadurch mit, welche Datenbank gemeint ist.

Drei Wege, eine Verbindung zu öffnen

// 1. URL with credentials as arguments
Connection a = DriverManager.getConnection(url, "app", "secret");

// 2. URL with a Properties bag (user, password, plus driver-specific keys)
Properties props = new Properties();
props.setProperty("user", "app");
props.setProperty("password", "secret");
props.setProperty("connectTimeout", "10");
Connection b = DriverManager.getConnection(url, props);

// 3. From a pooled DataSource (preferred for applications)
Connection c = dataSource.getConnection();

DriverManager vs. DataSource

DriverManager.getConnection(...) öffnet jedes Mal eine brandneue physische Verbindung und schließt sie, wenn Sie sie beenden. Dieser Handshake — DNS-Auflösung, TCP, TLS, Authentifizierung — kostet Dutzende von Millisekunden, was für ein Skript in Ordnung ist, aber für einen Server, der viele Anfragen verarbeitet, katastrophal wäre.

Eine DataSource, die durch einen Verbindungs-Pool (HikariCP, Apache DBCP oder einen von Ihrem App-Server bereitgestellten) unterstützt wird, hält eine Reihe physischer Verbindungen offen und gibt sie aus. Der Aufruf von getConnection() leiht eine aus; der Aufruf von close() gibt sie an den Pool zurück, anstatt sie wirklich zu schließen. Für jede langlebige Anwendung bevorzugen Sie eine gepoolte DataSource; greifen Sie auf DriverManager nur in kleinen Tools, Tests und Beispielen zurück.

Immer schließen — try-with-resources verwenden

Connection, Statement und ResultSet implementieren alle AutoCloseable. Wenn Sie sie in einem try-with-resources-Header deklarieren, wird garantiert, dass sie in umgekehrter Reihenfolge geschlossen werden, selbst wenn eine Ausnahme ausgelöst wird — die wichtigste Gewohnheit in JDBC:

try (Connection conn = DriverManager.getConnection(url, "app", "secret")) {
  // use conn...
} // conn.close() runs here automatically, even on exception

Das Verlieren von Verbindungen (vergessen zu schließen) erschöpft den Pool und hängt schließlich die gesamte Anwendung auf — ein klassischer Produktionsausfall. Beachten Sie, dass das Öffnen einer Connection eine geprüfte SQLException auslöst, sodass der Aufruf immer in einem try liegt (oder in einer Methode, die throws SQLException deklariert).

Sobald Sie eine Verbindung haben, verwenden Sie sie, um Statements und PreparedStatements zu erstellen — und diese sollten im selben try-with-resources-Header sitzen, damit sie vor der Verbindung geschlossen werden:

try (Connection conn = dataSource.getConnection();
     PreparedStatement ps = conn.prepareStatement("SELECT name FROM users WHERE id = ?")) {
  ps.setInt(1, 42);
  // ...read the ResultSet...
} // ps closes first, then conn — reverse declaration order

Eine Verbindung konfigurieren

Einmal geöffnet, trägt eine Verbindung sitzungsweite Einstellungen:

MethodeWas sie tut
setAutoCommit(false)Startet eine manuelle Transaktion; Sie rufen dann selbst commit() oder rollback() auf. Siehe JDBC-Transaktionen.
setTransactionIsolation(...)Wählt eine Isolationsstufe (z.B. TRANSACTION_READ_COMMITTED).
setReadOnly(true)Ein Hinweis, dass die Verbindung keine Schreibvorgänge ausführt; einige Treiber optimieren dafür.
setSchema(...) / setCatalog(...)Wählt den Namespace aus, gegen den Abfragen ausgeführt werden.
isValid(timeout)Gibt true zurück, wenn die Verbindung noch aktiv ist; Pools verwenden dies, um abgestorbene Verbindungen zu verwerfen.

Diese Einstellungen sind pro Sitzung und werden beim Schließen einer echten Verbindung zurückgesetzt — aber mit einem Pool kann eine ausgeliehene Verbindung noch Einstellungen tragen, die ein früherer Benutzer hinterlassen hat. Deshalb setzen Pools autoCommit und die Isolationsstufe beim Zurückgeben zurück, und deshalb sollten Sie die benötigten Einstellungen vornehmen, anstatt Standardwerte anzunehmen.

Häufige Verbindungsfehler

Wenn eine Verbindung fehlschlägt, erhalten Sie fast immer eine SQLException; die Meldung sagt Ihnen, welche Schicht betroffen ist:

  • No suitable driver found for ... — die Treiberklasse wurde nie geladen, oder das Subprotokoll der URL ist falsch geschrieben, sodass kein registrierter Treiber es beansprucht. Korrigieren Sie die Abhängigkeit oder die URL (siehe JDBC-Treiber).
  • Connection refused — auf diesem Host/Port lauscht nichts: Die Datenbank ist ausgefallen, oder Host/Port in der URL sind falsch.
  • Authentifizierung / password authentication failed — falscher user oder password, oder der Benutzer hat keine Rechte für diese Datenbank.
  • Verbindungs-Timeout — der Host ist nicht erreichbar (Firewall, falsches Netzwerk). Setzen Sie connectTimeout, damit der Aufruf schnell fehlschlägt, anstatt zu hängen.

Ein praktisches Beispiel: Die Anatomie einer Verbindungs-URL

Dieses Programm zerlegt eine realistische JDBC-URL in ihre Bestandteile und erstellt das Properties-Objekt, das Sie zusammen mit ihr übergeben würden — die beiden Eingaben, die getConnection benötigt — ohne eine laufende Datenbank zu erfordern.

java— editable, runs on the server

Was man aus dem Ergebnis mitnehmen kann:

  • Die URL ist nicht undurchsichtig — sie ist strukturierte Daten. getConnection parst genau diese Teile: Das Subprotokoll wählt den Treiber aus, und Host/Port/Datenbank teilen diesem Treiber mit, wo er sich verbinden soll. Eine URL laut zu lesen ist der schnellste Weg, "falsche Datenbank"-Fehler zu debuggen.
  • Der Query-String (?ssl=true&applicationName=reports) enthält treiberspezifische Optionen. Dieselben Einstellungen können in der URL oder im Properties-Objekt übergeben werden — beide erreichen den Treiber, und Sie mischen sie nach Belieben.
  • Zugangsdaten gehören in Properties (oder die DataSource-Konfiguration), nicht fest kodiert in den URL-String, den Sie protokollieren. Das Beispiel maskiert das Passwort bei der Ausgabe aus genau diesem Grund — protokollieren Sie niemals Zugangsdaten.
  • connectTimeout ist eine echte PostgreSQL-Treiber-Eigenschaft. Tuning-Einstellungen leben in diesen Schlüssel/Wert-Paaren, weshalb man selten etwas unterklassiert: Konfiguration, nicht Code, formt eine Verbindung.
  • Dies lief ohne Datenbank, weil das Erstellen der Eingaben für getConnection reine String-Arbeit ist. Der teure Teil — der Socket und die Server-Sitzung — findet nur beim getConnection-Aufruf selbst statt, weshalb Sie ihn verzögern und schnell schließen.

Übungen

Übung
Warum sollte eine JDBC-Connection fast immer innerhalb einer try-with-resources-Anweisung angefordert werden?
Warum sollte eine JDBC-Connection fast immer innerhalb einer try-with-resources-Anweisung angefordert werden?
Was this page helpful?