⌘+k ctrl+k
1.4 (LTS)
搜索快捷键 cmd + k | ctrl + k
Java JDBC 客户端

DuckDB Java (JDBC) 客户端的最新稳定版本为 1.4.4。

安装

DuckDB Java JDBC API 可从 Maven Central 安装。详情请参阅安装页面

基本 API 用法

DuckDB 的 JDBC API 实现了标准 Java 数据库连接 (JDBC) API 4.1 版本的主要部分。此处不对 JDBC 进行详述,详情请参阅官方文档。以下内容重点介绍 DuckDB 的特有部分。

有关我们对 JDBC 规范的扩展的更多信息,请参考外部托管的 API 参考文档,或查看下方的 Arrow 方法

启动与关闭

在 JDBC 中,数据库连接通过标准的 java.sql.DriverManager 类创建。驱动程序应在 DriverManager 中自动注册;如果由于某种原因无法自动注册,您可以使用以下语句强制注册:

Class.forName("org.duckdb.DuckDBDriver");

要创建 DuckDB 连接,请使用 jdbc:duckdb: JDBC URL 前缀调用 DriverManager,如下所示:

import java.sql.Connection;
import java.sql.DriverManager;

Connection conn = DriverManager.getConnection("jdbc:duckdb:");

要使用 Appender 等 DuckDB 特有功能,请将对象强制转换为 DuckDBConnection

import java.sql.DriverManager;
import org.duckdb.DuckDBConnection;

DuckDBConnection conn = (DuckDBConnection) DriverManager.getConnection("jdbc:duckdb:");

当仅使用 jdbc:duckdb: URL 时,将创建一个内存数据库。请注意,内存数据库的数据不会持久化到磁盘(即当您退出 Java 程序时,所有数据都会丢失)。如果您希望访问或创建持久化数据库,请在路径后附加文件名。例如,如果您的数据库存储在 /tmp/my_database 中,请使用 JDBC URL jdbc:duckdb:/tmp/my_database 来创建连接。

可以以只读模式打开 DuckDB 数据库文件。例如,如果多个 Java 进程需要同时读取同一个数据库文件,这就非常有用。要以只读模式打开现有的数据库文件,请设置连接属性 duckdb.read_only,如下所示:

Properties readOnlyProperty = new Properties();
readOnlyProperty.setProperty("duckdb.read_only", "true");
Connection conn = DriverManager.getConnection("jdbc:duckdb:/tmp/my_database", readOnlyProperty);

可以使用 DriverManager 创建额外的连接。一种更高效的机制是调用 DuckDBConnection#duplicate() 方法。

Connection conn2 = ((DuckDBConnection) conn).duplicate();

支持多个连接,但不支持混合使用读写连接和只读连接。

配置连接

可以提供配置选项来更改数据库系统的各种设置。请注意,其中许多设置稍后也可以使用 PRAGMA 语句进行更改。

Properties connectionProperties = new Properties();
connectionProperties.setProperty("temp_directory", "/path/to/temp/dir/");
Connection conn = DriverManager.getConnection("jdbc:duckdb:/tmp/my_database", connectionProperties);

查询

DuckDB 支持使用标准的 JDBC 方法发送查询和检索结果集。首先必须从 Connection 创建一个 Statement 对象,然后可以使用该对象通过 executeexecuteQuery 发送查询。execute() 用于不需要返回结果的查询,如 CREATE TABLEUPDATE 等;executeQuery() 用于产生结果的查询(例如 SELECT)。以下是两个示例。另请参阅 JDBC StatementResultSet 文档。

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 table
Statement stmt = conn.createStatement();
stmt.execute("CREATE TABLE items (item VARCHAR, value DECIMAL(10, 2), count INTEGER)");
// insert two items into the table
stmt.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();
jeans
1
hammer
2

DuckDB 还根据 JDBC API 支持预编译语句(Prepared Statements)。

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
}

警告:请不要使用预编译语句将大量数据插入 DuckDB。请参阅数据导入文档以获取更好的方案。

Arrow 方法

请参阅 API 参考文档了解类型签名。

Arrow 导出

以下示例演示了如何导出 Arrow 流并使用 Java Arrow 绑定进行消费:

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 导入

以下示例演示了如何从 Java Arrow 绑定消费 Arrow 流。

import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.ipc.ArrowReader;
import org.duckdb.DuckDBConnection;

// Arrow binding
try (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));
            }
        }
    }
}

流式传输结果

在 JDBC 驱动程序中,结果流式传输是可选功能——通过在运行查询前将 jdbc_stream_results 配置设置为 true。最简单的方法是在 Properties 对象中传递该配置。

Properties props = new Properties();
props.setProperty(DuckDBDriver.JDBC_STREAM_RESULTS, String.valueOf(true));

Connection conn = DriverManager.getConnection("jdbc:duckdb:", props);

Appender

DuckDB JDBC 驱动程序通过 org.duckdb.DuckDBAppender 类提供 Appender 功能。该类的构造函数需要模式名称(Schema Name)和表名。当调用 close() 方法时,Appender 会被刷新(Flush)。

示例

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 scope
try (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)

DuckDB JDBC 驱动程序提供批量写入功能。批量写入器支持预编译语句,以减轻查询解析的开销。

批量插入的首选方法是使用 Appender,因为它性能更高。但是,当无法使用 Appender 时,可以使用批量写入器作为替代方案。

使用预编译语句的批量写入器

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();

使用普通语句的批量写入器

批量写入器还支持普通的 SQL 语句。

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();

故障排除

找不到驱动程序类

如果 Java 应用程序无法找到 DuckDB,它可能会抛出以下错误:

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)
    ...

当尝试手动加载类时,可能会导致此错误:

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)
    ...

这些错误源于未检测到 DuckDB Maven/Gradle 依赖项。为确保已检测到,请在您的 IDE 中强制刷新 Maven 配置。

© 2025 DuckDB 基金会,阿姆斯特丹,荷兰
行为准则 商标使用指南