Java-Client (JDBC)
Installation Um den DuckDB-Java-Client (JDBC) zu verwenden, besuchen Sie die Java-Installationsseite.
Die aktuellste stabile Version des DuckDB-Java-Clients (JDBC) ist 1.5.5.
Installation
Die DuckDB-Java-JDBC-API kann von Maven Central installiert werden. Details finden Sie auf der Installationsseite.
Grundlegende API-Nutzung
Die JDBC-API von DuckDB implementiert die wesentlichen Teile der standardmäßigen Java-Database-Connectivity-API (JDBC), Version 4.1. JDBC selbst zu beschreiben, sprengt den Rahmen dieser Seite; Details finden Sie in der offiziellen Dokumentation. Im Folgenden konzentrieren wir uns auf die DuckDB-spezifischen Teile.
Weitere Informationen zu unseren Erweiterungen der JDBC-Spezifikation finden Sie in der extern gehosteten API-Referenz oder unten unter Arrow-Methoden.
Start und Beenden
In JDBC werden Datenbankverbindungen über die Standardklasse java.sql.DriverManager erstellt.
Der Treiber sollte sich automatisch im DriverManager registrieren; falls das aus irgendeinem Grund nicht funktioniert, können Sie die Registrierung mit der folgenden Anweisung erzwingen:
Class.forName("org.duckdb.DuckDBDriver");Um eine DuckDB-Verbindung zu erstellen, rufen Sie DriverManager mit dem JDBC-URL-Präfix jdbc:duckdb: auf, etwa so:
import java.sql.Connection;import java.sql.DriverManager;
Connection conn = DriverManager.getConnection("jdbc:duckdb:");Um DuckDB-spezifische Funktionen wie den Appender zu nutzen, casten Sie das Objekt zu einer DuckDBConnection:
import java.sql.DriverManager;import org.duckdb.DuckDBConnection;
DuckDBConnection conn = (DuckDBConnection) DriverManager.getConnection("jdbc:duckdb:");Wenn Sie nur die URL jdbc:duckdb: verwenden, wird eine In-Memory-Datenbank erstellt. Beachten Sie, dass bei einer In-Memory-Datenbank keine Daten auf die Festplatte geschrieben werden (d. h. alle Daten gehen verloren, wenn Sie das Java-Programm beenden). Wenn Sie auf eine persistente Datenbank zugreifen oder eine solche erstellen möchten, hängen Sie den Dateinamen an den Pfad an. Wenn Ihre Datenbank beispielsweise in /tmp/my_database gespeichert ist, verwenden Sie die JDBC-URL jdbc:duckdb:/tmp/my_database, um eine Verbindung dazu herzustellen.
Es ist möglich, eine DuckDB-Datenbankdatei im Nur-Lese-Modus zu öffnen. Das ist beispielsweise nützlich, wenn mehrere Java-Prozesse dieselbe Datenbankdatei gleichzeitig lesen sollen. Um eine vorhandene Datenbankdatei im Nur-Lese-Modus zu öffnen, setzen Sie die Verbindungseigenschaft duckdb.read_only wie folgt:
Properties readOnlyProperty = new Properties();readOnlyProperty.setProperty("duckdb.read_only", "true");Connection conn = DriverManager.getConnection("jdbc:duckdb:/tmp/my_database", readOnlyProperty);Weitere Verbindungen können über den DriverManager erstellt werden. Ein effizienterer Mechanismus ist der Aufruf der Methode DuckDBConnection#duplicate():
Connection conn2 = ((DuckDBConnection) conn).duplicate();Mehrere Verbindungen sind erlaubt, das Mischen von Lese-/Schreib- und Nur-Lese-Verbindungen wird jedoch nicht unterstützt.
Verbindungen konfigurieren
Konfigurationsoptionen können angegeben werden, um verschiedene Einstellungen des Datenbanksystems zu ändern. Beachten Sie, dass viele dieser
Einstellungen später auch mit PRAGMA-Anweisungen geändert werden können.
Properties connectionProperties = new Properties();connectionProperties.setProperty("temp_directory", "/path/to/temp/dir/");Connection conn = DriverManager.getConnection("jdbc:duckdb:/tmp/my_database", connectionProperties);Abfragen
DuckDB unterstützt die standardmäßigen JDBC-Methoden zum Senden von Abfragen und zum Abrufen von Ergebnismengen. Zuerst muss aus der Connection ein Statement-Objekt erstellt werden; dieses Objekt kann dann verwendet werden, um Abfragen mit execute und executeQuery zu senden. execute() ist für Abfragen gedacht, bei denen keine Ergebnisse erwartet werden, etwa CREATE TABLE oder UPDATE usw., und executeQuery() ist für Abfragen gedacht, die Ergebnisse liefern (z. B. SELECT). Unten zwei Beispiele. Siehe auch die JDBC-Dokumentationen zu Statement und ResultSet.
import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;
Connection conn = DriverManager.getConnection("jdbc:duckdb:");
// create a tableStatement stmt = conn.createStatement();stmt.execute("CREATE TABLE items (item VARCHAR, value DECIMAL(10, 2), count INTEGER)");// insert two items into the tablestmt.execute("INSERT INTO items VALUES ('jeans', 20.0, 1), ('hammer', 42.2, 2)");
try (ResultSet rs = stmt.executeQuery("SELECT * FROM items")) { while (rs.next()) { System.out.println(rs.getString(1)); System.out.println(rs.getInt(3)); }}stmt.close();jeans1hammer2DuckDB unterstützt außerdem vorbereitete Anweisungen gemäß der JDBC-API:
import java.sql.PreparedStatement;
try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO items VALUES (?, ?, ?);")) { stmt.setString(1, "chainsaw"); stmt.setDouble(2, 500.0); stmt.setInt(3, 42); stmt.execute(); // more calls to execute() possible}Warning Verwenden Sie keine vorbereiteten Anweisungen, um große Datenmengen in DuckDB einzufügen. Bessere Optionen finden Sie in der Dokumentation zum Datenimport.
Arrow-Methoden
Typsignaturen finden Sie in der API-Referenz
Arrow-Export
Das Folgende zeigt den Export eines Arrow-Streams und dessen Verbrauch über die Java-Arrow-Bindings.
import org.apache.arrow.memory.RootAllocator;import org.apache.arrow.vector.ipc.ArrowReader;import org.duckdb.DuckDBResultSet;
try (var conn = DriverManager.getConnection("jdbc:duckdb:"); var stmt = conn.prepareStatement("SELECT * FROM generate_series(2000)"); var resultset = (DuckDBResultSet) stmt.executeQuery(); var allocator = new RootAllocator()) { try (var reader = (ArrowReader) resultset.arrowExportStream(allocator, 256)) { while (reader.loadNextBatch()) { System.out.println(reader.getVectorSchemaRoot().getVector("generate_series")); } } stmt.close();}Arrow-Import
Das Folgende zeigt den Verbrauch eines Arrow-Streams aus den Java-Arrow-Bindings.
import org.apache.arrow.memory.RootAllocator;import org.apache.arrow.vector.ipc.ArrowReader;import org.duckdb.DuckDBConnection;
// Arrow bindingtry (var allocator = new RootAllocator(); ArrowStreamReader reader = null; // should not be null of course var arrow_array_stream = ArrowArrayStream.allocateNew(allocator)) { Data.exportArrayStream(allocator, reader, arrow_array_stream);
// DuckDB setup try (var conn = (DuckDBConnection) DriverManager.getConnection("jdbc:duckdb:")) { conn.registerArrowStream("asdf", arrow_array_stream);
// run a query try (var stmt = conn.createStatement(); var rs = (DuckDBResultSet) stmt.executeQuery("SELECT count(*) FROM asdf")) { while (rs.next()) { System.out.println(rs.getInt(1)); } } }}Streaming-Ergebnisse
Ergebnis-Streaming ist im JDBC-Treiber opt-in – indem Sie die Konfiguration jdbc_stream_results vor dem Ausführen einer Abfrage auf true setzen. Am einfachsten übergeben Sie das im Properties-Objekt.
Properties props = new Properties();props.setProperty(DuckDBDriver.JDBC_STREAM_RESULTS, String.valueOf(true));
Connection conn = DriverManager.getConnection("jdbc:duckdb:", props);Appender
Der Appender ist im DuckDB-JDBC-Treiber über die Klasse org.duckdb.DuckDBAppender verfügbar.
Der Konstruktor der Klasse benötigt den Schemanamen und den Tabellennamen, auf den er angewendet wird.
Der Appender wird geleert, wenn die Methode close() aufgerufen wird.
Beispiel:
import java.sql.DriverManager;import java.sql.Statement;import org.duckdb.DuckDBConnection;
DuckDBConnection conn = (DuckDBConnection) DriverManager.getConnection("jdbc:duckdb:");try (var stmt = conn.createStatement()) { stmt.execute("CREATE TABLE tbl (x BIGINT, y FLOAT, s VARCHAR)");
// using try-with-resources to automatically close the appender at the end of the scopetry (var appender = conn.createAppender(DuckDBConnection.DEFAULT_SCHEMA, "tbl")) { appender.beginRow(); appender.append(10); appender.append(3.2); appender.append("hello"); appender.endRow(); appender.beginRow(); appender.append(20); appender.append(-8.1); appender.append("world"); appender.endRow();}Batch Writer
Der DuckDB-JDBC-Treiber bietet Batch-Schreibfunktionalität. Der Batch Writer unterstützt vorbereitete Anweisungen, um den Aufwand des Query-Parsings zu verringern.
Die bevorzugte Methode für Masseneinfügungen ist der Appender aufgrund seiner höheren Leistung. Wenn der Appender jedoch nicht verwendet werden kann, steht der Batch Writer als Alternative zur Verfügung.
Batch Writer mit vorbereiteten Anweisungen
import java.sql.DriverManager;import java.sql.PreparedStatement;import org.duckdb.DuckDBConnection;
DuckDBConnection conn = (DuckDBConnection) DriverManager.getConnection("jdbc:duckdb:");PreparedStatement stmt = conn.prepareStatement("INSERT INTO test (x, y, z) VALUES (?, ?, ?);");
stmt.setObject(1, 1);stmt.setObject(2, 2);stmt.setObject(3, 3);stmt.addBatch();
stmt.setObject(1, 4);stmt.setObject(2, 5);stmt.setObject(3, 6);stmt.addBatch();
stmt.executeBatch();stmt.close();Batch Writer mit einfachen Anweisungen
Der Batch Writer unterstützt auch einfache SQL-Anweisungen:
import java.sql.DriverManager;import java.sql.Statement;import org.duckdb.DuckDBConnection;
DuckDBConnection conn = (DuckDBConnection) DriverManager.getConnection("jdbc:duckdb:");Statement stmt = conn.createStatement();
stmt.execute("CREATE TABLE test (x INTEGER, y INTEGER, z INTEGER)");
stmt.addBatch("INSERT INTO test (x, y, z) VALUES (1, 2, 3);");stmt.addBatch("INSERT INTO test (x, y, z) VALUES (4, 5, 6);");
stmt.executeBatch();stmt.close();Fehlerbehebung
Treiberklasse nicht gefunden
Wenn die Java-Anwendung den DuckDB-Treiber nicht findet, kann sie den folgenden Fehler auslösen:
Exception in thread "main" java.sql.SQLException: No suitable driver found for jdbc:duckdb: at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:706) at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:252) ...Und beim manuellen Laden der Klasse kann dieser Fehler entstehen:
Exception in thread "main" java.lang.ClassNotFoundException: org.duckdb.DuckDBDriver at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) at java.base/java.lang.Class.forName0(Native Method) at java.base/java.lang.Class.forName(Class.java:375) ...Diese Fehler entstehen, weil die DuckDB-Maven-/Gradle-Abhängigkeit nicht erkannt wird. Damit sie erkannt wird, erzwingen Sie in Ihrer IDE eine Aktualisierung der Maven-Konfiguration.