JDBC driver (Java)¶
DecentDB ships an in-tree Java stack under bindings/java/:
bindings/java/driver/— JDBC driver (com.decentdb.jdbc.DecentDBDriver)bindings/java/native/— JNI bridge (libdecentdb_jni.*)bindings/java/dbeaver-extension/— DBeaver packagingtests/bindings/java/Smoke.java— low-level FFM smoke for the raw C ABI
The JDBC driver targets the stable DecentDB C ABI through JNI. Public behavior should match the engine and C ABI, rather than inventing Java-only semantics.
Supported JDBC URLs¶
The driver accepts URLs of the form:
jdbc:decentdb:/absolute/path/to/db.ddb
jdbc:decentdb:/absolute/path/to/db.ddb?mode=open
jdbc:decentdb:/absolute/path/to/db.ddb?readOnly=true
Supported connection properties:
mode—openOrCreate(default),open, orcreatereadOnly—trueorfalse(defaultfalse)
Currently unsupported at open time:
busyTimeoutMscachePages
The stable C ABI currently exposes only default create, open, and open_or_create entry points. The Java driver therefore rejects busyTimeoutMs and cachePages instead of silently pretending they work.
Build locally¶
From the repository root:
This builds the core shared library, the JNI bridge, and the JDBC jar. The jar embeds the matching native libraries for the current OS/arch from target/release/.
Validate the Java binding¶
The Java build itself targets Java 17 bytecode. In this repository, the Gradle test path is currently validated with JDK 21:
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
export PATH="$JAVA_HOME/bin:$PATH"
cd bindings/java
./gradlew :driver:test
Run the standalone JDBC example¶
The driver now includes a runnable CRUD example that covers schema creation, prepared statements, DECIMAL, BOOL, TIMESTAMP, rollback, metadata, and basic error handling:
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
export PATH="$JAVA_HOME/bin:$PATH"
cd bindings/java
./gradlew :driver:runCrudExample
To target a specific database path:
Run the benchmark¶
From bindings/java/:
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
export PATH="$JAVA_HOME/bin:$PATH"
./gradlew :driver:benchmarkFetch -PbenchmarkArgs="--count 100000 --point-reads 5000 --fetchmany-batch 1024 --db-prefix java_bench_fetch"
Supported benchmark options:
--engine <all|decentdb|sqlite>--count <n>--point-reads <n>--fetchmany-batch <n>--point-seed <n>--db-prefix <prefix>--sqlite-jdbc <jar_path>--keep-db
When --engine all is used, the benchmark will try to auto-discover a local sqlite-jdbc jar from common DBeaver, Rider, and Maven cache locations. You can also pass it explicitly with --sqlite-jdbc.
Run the low-level FFM smoke¶
tests/bindings/java/Smoke.java validates the raw C ABI independently of the JDBC driver. With JDK 21, this path uses the preview Foreign Function & Memory API:
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
export PATH="$JAVA_HOME/bin:$PATH"
cargo build -p decentdb
javac --enable-preview --release 21 tests/bindings/java/Smoke.java
java --enable-preview --enable-native-access=ALL-UNNAMED -cp tests/bindings/java Smoke
The smoke test auto-detects target/debug/ or target/release/ for the core shared library.
JDBC usage example¶
import com.decentdb.jdbc.DecentDBConnection;
import com.decentdb.jdbc.DecentDBDataSource;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.time.Instant;
DecentDBDataSource dataSource = new DecentDBDataSource("jdbc:decentdb:/tmp/shop.ddb");
dataSource.setMode("openOrCreate");
try (Connection connection = dataSource.getConnection()) {
connection.setAutoCommit(false);
try (PreparedStatement create = connection.prepareStatement(
"CREATE TABLE IF NOT EXISTS products (" +
"id INT64 PRIMARY KEY, " +
"name TEXT NOT NULL, " +
"price DECIMAL(12,2) NOT NULL, " +
"active BOOL NOT NULL, " +
"updated_at TIMESTAMP)"
)) {
create.executeUpdate();
}
try (PreparedStatement insert = connection.prepareStatement(
"INSERT INTO products (id, name, price, active, updated_at) VALUES ($1, $2, $3, $4, $5)"
)) {
insert.setLong(1, 1L);
insert.setString(2, "Keyboard");
insert.setBigDecimal(3, new BigDecimal("129.99"));
insert.setBoolean(4, true);
insert.setTimestamp(5, Timestamp.from(Instant.now()));
insert.executeUpdate();
}
connection.commit();
try (PreparedStatement query = connection.prepareStatement(
"SELECT id, name, price FROM products WHERE id = $1"
)) {
query.setLong(1, 1L);
try (ResultSet rs = query.executeQuery()) {
while (rs.next()) {
System.out.println(rs.getString("name") + " => " + rs.getBigDecimal("price"));
}
}
}
if (connection instanceof DecentDBConnection decent) {
System.out.println("Engine version: " + decent.getEngineVersion());
decent.checkpoint();
}
}
DecentDB's Java driver currently follows the engine's native positional placeholder style ($1, $2, ...) in prepared SQL.
DecentDB-specific connection helpers¶
DecentDBConnection adds a small number of engine-truth helpers beyond the standard JDBC Connection surface:
isInTransaction()— queries the engine viaddb_db_in_transactiongetAbiVersion()— exposesddb_abi_versiongetEngineVersion()— exposesddb_versioncheckpoint()— runsddb_db_checkpointsaveAs(path)— runsddb_db_save_as
saveAs(path) requires a destination path that does not already exist.
Thread-safety and pooling¶
DecentDB uses a single-process, one-writer / many-readers model.
Connectionmethods are serialized internally per connectionStatement,PreparedStatement, andResultSetare not thread-safeDecentDBDataSourceis a thin configuration wrapper, not a pooling implementation
If you place DecentDB behind an external pool, keep write-heavy workloads at a maximum size of 1.
Metadata and framework support¶
The driver includes:
- a
DataSourceimplementation:com.decentdb.jdbc.DecentDBDataSource - JDBC metadata for tables, columns, indexes, keys, and type mapping
- DecentDB-specific schema helpers on
DecentDBDatabaseMetaDatafor table DDL and trigger listing
For DBeaver integration details, see the DBeaver guide.