JDBC IndexStore
JdbcIndexStore adapts blocking JDBC databases to R2D1’s asynchronous IndexStore SPI. It stores
document identifiers and indexed field projections only. The authoritative serialized document
remains in the configured DocumentStore.
Supported databases
| Database | Supported topology |
|---|---|
| H2 | Persistent embedded file and TCP server databases |
| HSQLDB | Persistent embedded file and HSQL server databases |
| SQLite | Local persistent embedded file database only |
The module selects a built-in dialect from the exact JDBC product name. Other database products fail
before schema mutation. The three JDBC drivers are not bundled or exposed transitively; the
application supplies the driver selected by its DataSource.
Construct the store
implementation("dev.nexcraft:r2d1-jdbc:<version>")
Create a bounded execution resource for blocking JDBC work and pass the application-owned
DataSource to the store:
import dev.nexcraft.r2d1.jdbc.JdbcExecution;
import dev.nexcraft.r2d1.jdbc.JdbcIndexStore;
import javax.sql.DataSource;
DataSource dataSource = applicationDataSource;
try (JdbcExecution execution = JdbcExecution.create(4, 64)) {
JdbcIndexStore indexes = new JdbcIndexStore(dataSource, execution);
// Pass indexes and indexes::initialize to PersistenceCollectionFactory.
}
JdbcIndexStore owns neither the DataSource nor the JdbcExecution. JdbcExecution.create(...)
owns the executor it creates and must be closed by its creator. JdbcExecution.using(...) wraps a
caller-owned executor and never shuts it down.
Execution mode
Platform threads are the default. Virtual threads are an explicit Java 25 or newer option and never fall back silently:
JdbcExecutionConfig config =
new JdbcExecutionConfig(JdbcExecutionMode.VIRTUAL_THREAD, 100, 500);
JdbcExecution execution = JdbcExecution.create(config);
maxConcurrency limits running operations and maxPending limits admitted work waiting to run.
These limits do not select a database topology, change the number of physical connections, or
replace a connection pool.
Schema and values
Each collection maps to one table with a document_id primary key and one required column per
@Index field. Required single-column indexes are created or validated; existing compatible schema
objects are reused. Initialization does not drop, rename, or convert existing objects.
The supported logical indexed values are String, long/Long, double/Double, and
boolean/Boolean. Every indexed value must be present and non-null when a document is written.
Instant is not supported as an indexed value by the built-in dialects.
SQLite boundary
SQLite is supported as a local persistent file database. It is not a remote SQLite service. R2D1
applies PRAGMA busy_timeout=5000 to operation connections and coordinates writes within one
JdbcIndexStore instance. Separate store instances and external processes still coordinate through
SQLite’s locking behavior.
Query and lifecycle behavior
JDBC queries use prepared statements, AND-combine multiple filters, and use keyset cursors. An
explicit sort is followed by document_id in the same direction; a query without an explicit sort
uses document_id ASC. See Querying for the public builder and
Configuration for ownership boundaries.
