This is a minimal (~130KB, zero dependencies), Type 4 JDBC driver for rqlite, a lightweight, distributed relational database built on top of SQLite.
This driver enables Java applications to interact with rqlite
over HTTP, supporting standard
JDBC operations like queries, updates, and batch processing in a clustered environment.
- JDBC Compliance: Supports core JDBC APIs, including
Connection
,Statement
,PreparedStatement
, andResultSet
. - Atomic Transactions: Executes multiple statements atomically using rqlite’s
transaction=true
mode via batch operations. - Clustered Environment Support: Configurable options for read consistency, write queuing, and timeouts to handle rqlite’s distributed nature.
- Schema and Metadata Access: Query table metadata, primary keys, foreign keys, and indexes (see L4DriverTest).
- Java 11 or higher
rqlite
server running (e.g.,http://localhost:4001
)
Install from Maven Central
io.rqlite:rqlite-jdbc:[version]
The driver version corresponds to the last known rqlite
release the driver was tested against, followed by a build version of the driver itself.
Connect to an rqlite
instance and execute queries using standard JDBC APIs.
import java.sql.*;
var url = "jdbc:sqlite:http://localhost:4001";
try (Connection conn = DriverManager.getConnection(url)) {
var stmt = conn.createStatement();
stmt.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)");
var ps = conn.prepareStatement("INSERT INTO users (name, age) VALUES (?, ?)");
ps.setString(1, "Alice");
ps.setInt(2, 30);
ps.executeUpdate();
var rs = stmt.executeQuery("SELECT * FROM users");
while (rs.next()) {
System.out.println("ID: " + rs.getInt("id") + ", Name: " + rs.getString("name") + ", Age: " + rs.getInt("age"));
}
}
rqlite
executes statements atomically with transaction=true
. Use batch operations for multi-statement transactions.
Using Statement
:
Statement stmt = conn.createStatement();
stmt.addBatch("INSERT INTO users (name, age) VALUES ('Fiona', 25)");
stmt.addBatch("INSERT INTO users (name, age) VALUES ('Sinead', 28)");
int[] updateCounts = stmt.executeBatch(); // Executes atomically
Using PreparedStatement
:
PreparedStatement ps = conn.prepareStatement("INSERT INTO users (name, age) VALUES (?, ?)");
ps.setString(1, "Fiona");
ps.setInt(2, 25);
ps.addBatch();
ps.setString(1, "Sinead");
ps.setInt(2, 28);
ps.addBatch();
int[] updateCounts = ps.executeBatch(); // Executes atomically
See L4PsTest for advanced examples with various data types, streams, and LOBs.
Customize the driver’s behavior via JDBC URL parameters, see L4Options. Below are the available options, their defaults, and their purposes.
These options come from rqlite
's Developer Guide
Property Key | Type | Default Value | Description |
---|---|---|---|
baseUrl |
String |
null |
The base URL of the RQLite server (e.g., http://localhost:4001 ). |
user |
String |
null |
Username for RQLite server authentication. |
password |
String |
null |
Password for RQLite server authentication. |
cacert |
String |
null |
Path to the CA certificate for SSL/TLS connections. |
insecure |
boolean |
false |
If true , disables SSL/TLS verification (not recommended for production). |
timeoutSec |
long |
5 |
Timeout for HTTP requests in seconds. |
queue |
boolean |
false |
If true , enables queuing of requests on the RQLite server. |
wait |
boolean |
true |
If true , waits for the request to be processed by the RQLite leader. |
level |
L4Level |
L4Level.linearizable |
Consistency level for queries (none , weak , strong , linearizable ). |
linearizableTimeoutSec |
long |
5 |
Timeout for linearizable consistency queries in seconds. |
freshnessSec |
long |
5 |
Maximum age of data for freshness-based queries in seconds. |
freshnessStrict |
boolean |
false |
If true , enforces strict freshness for queries. |
Example JDBC URL:
String url = "jdbc:sqlite:http://localhost:4001?timeoutSec=5&level=strong&freshnessSec=1";
Result sets are held in memory (mapped from rqlite’s JSON responses to JDBC ResultSet). Write queries that return small datasets to avoid memory issues.
Only the main
SQLite database is reported as a catalog to JDBC.
The driver offers deferred transaction support on Connection
instances due to rqlite
's Transaction support conventions, which deviate from the JDBC standard.
To execute multiple SQL statements as a transaction, you have two options.
Populate a JDBC batch using Statement
or PreparedStatement
, which will get sent with transaction=true
to the underlying rqlite
HTTP request.
Call setAutoCommit(false)
on a Connection
, and run insert
, update
or delete
statements.
The execution of these statements will get deferred until you call commit()
.
This will send all statements to the database as a single batch, appending transaction=true
to the underlying rqlite
HTTP request.
This implies that you won't be able to inspect ResultSet
s, metadata or row counts after executing each statement. A dummy resultset is provided only for compatibility with JDBC semantics.
The only guarantee is that if commit()
succeeds, then all deferred statements were accepted by the database.
Lastly, make sure that all statements get executed through the same connection where the transaction was initiated.
Only TRANSACTION_SERIALIZABLE
is supported, with linearizable
read consistency by default. Setting level=weak
or level=none
may introduce read inconsistencies.
User-defined SQL types (UDTs) are not supported. getTypeMap
and setTypeMap
are implemented for compliance but have no effect.
Contributions are welcome! Please submit issues or pull requests to this GitHub repository.
Requires Gradle 8.1 or later.
Create a file with the following content at ~/.gsOrgConfig.json
:
{
"orgConfigUrl": "https://raw.githubusercontent.com/rqlite/rqlite-jdbc/refs/heads/org-config/org-config.json"
}
Then run:
gradle clean build