We've released oxidb-jdbc 0.1.0 — a pure Java JDBC driver that speaks OxiDB's native OxiWire binary protocol. It's a single 38 KB JAR with zero dependencies, works with Java 17+, and lets you connect any JDBC-compatible tool to OxiDB: DBeaver, IntelliJ Database Tools, DataGrip, or your own Java/Kotlin/Scala applications.

Why a JDBC Driver?

JDBC is the standard database connectivity API in the Java ecosystem. By implementing a JDBC driver, OxiDB becomes accessible to:

  • GUI database tools — DBeaver, DataGrip, IntelliJ IDEA, DbVisualizer, SQuirreL SQL
  • Java applications — any app using java.sql or frameworks like Spring JDBC, MyBatis, jOOQ
  • BI & reporting tools — tools that accept JDBC connections for data analysis
  • ETL pipelines — tools like Apache NiFi, Talend, and Pentaho that use JDBC sources

OxiWire Binary Protocol

Unlike many database drivers that use text-based protocols, the OxiDB JDBC driver communicates using OxiWire — OxiDB's custom binary wire protocol. This provides several advantages over JSON:

  • No serialization overhead — values are encoded directly as typed binary (integers as 8-byte LE, floats as IEEE 754, strings as length-prefixed UTF-8)
  • Type preservation — booleans, integers, floats, nulls, arrays, and maps each have a distinct type tag. No ambiguity between "42" the string and 42 the number
  • Compact encoding — no JSON delimiters, quotes, or escaping. A map with 5 fields is just [0x08][count][key1][val1]...

Protocol Format

Every OxiWire message follows this structure:

Framing:   [4 bytes LE length] [payload]
Request:   [0xDB magic] [encoded map value]
Response:  [0xDB magic] [status: 0=ok, 1=error] [encoded value]

Type Tags:
  0x00 = Null
  0x01 = Boolean false
  0x02 = Boolean true
  0x03 = Int64  (8 bytes little-endian)
  0x04 = Uint64 (8 bytes little-endian)
  0x05 = Float64 (8 bytes IEEE 754 LE)
  0x06 = String (4-byte LE length + UTF-8 bytes)
  0x07 = Array  (4-byte LE count + encoded values)
  0x08 = Map    (4-byte LE count + bare-string keys + encoded values)

Map keys are "bare strings" — they don't carry a type tag since keys are always strings. This saves one byte per key.

Driver Architecture

The driver consists of 8 Java classes, all in the com.oxidb.jdbc package:

ClassJDBC InterfaceResponsibility
OxiDbDriverjava.sql.DriverURL parsing, connection factory. Registered via META-INF/services
OxiDbConnectionjava.sql.ConnectionTCP socket management, OxiWire framing, command dispatch
OxiDbStatementjava.sql.StatementSQL execution via {"cmd":"sql","query":"..."}
OxiDbPreparedStatementjava.sql.PreparedStatementParameter substitution into SQL
OxiDbResultSetjava.sql.ResultSetScrollable result set backed by decoded document maps
OxiDbResultSetMetaDatajava.sql.ResultSetMetaDataColumn type inference from values
OxiDbDatabaseMetaDatajava.sql.DatabaseMetaDataTable listing via list_collections, column discovery
OxiWireCodecBinary encoder/decoder for all OxiWire types

Connection URL

The JDBC URL format is:

jdbc:oxidb://host:port

// Examples
jdbc:oxidb://127.0.0.1:4444      // local server
jdbc:oxidb://db.example.com:4444  // remote server
jdbc:oxidb://oxipool:4445         // via OxiPool connection pooler

The driver class is com.oxidb.jdbc.OxiDbDriver and is auto-discovered via the Java ServiceLoader mechanism (META-INF/services/java.sql.Driver).

Java Usage

Basic Query

import java.sql.*;

// Driver is auto-registered via ServiceLoader
Connection conn = DriverManager.getConnection("jdbc:oxidb://127.0.0.1:4444");

// Run SQL queries
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users WHERE age > 25");

while (rs.next()) {
    System.out.println(rs.getString("name") + " - " + rs.getInt("age"));
}

rs.close();
stmt.close();
conn.close();

Insert Data

Statement stmt = conn.createStatement();
stmt.executeUpdate("INSERT INTO users (name, email, age) VALUES ('Alice', '[email protected]', 30)");
System.out.println("Inserted: " + stmt.getUpdateCount());

PreparedStatement

PreparedStatement ps = conn.prepareStatement("SELECT * FROM orders WHERE status = ? AND total > ?");
ps.setString(1, "shipped");
ps.setDouble(2, 100.0);
ResultSet rs = ps.executeQuery();

while (rs.next()) {
    System.out.printf("Order %s: $%.2f%n", rs.getString("_id"), rs.getDouble("total"));
}

Create Table & Index

Statement stmt = conn.createStatement();
stmt.executeUpdate("CREATE TABLE products (name, price, category)");
stmt.executeUpdate("CREATE INDEX idx_category ON products (category)");
stmt.executeUpdate("INSERT INTO products (name, price, category) VALUES ('Widget', 9.99, 'tools')");

Database Metadata

DatabaseMetaData meta = conn.getMetaData();
System.out.println("Database: " + meta.getDatabaseProductName());   // "OxiDB"
System.out.println("Version: " + meta.getDatabaseProductVersion()); // "0.18.0"

// List all tables (collections)
ResultSet tables = meta.getTables(null, null, "%", null);
while (tables.next()) {
    System.out.println("Table: " + tables.getString("TABLE_NAME"));
}

// List columns of a specific table
ResultSet cols = meta.getColumns(null, null, "users", "%");
while (cols.next()) {
    System.out.printf("  %s (%s)%n", cols.getString("COLUMN_NAME"), cols.getString("TYPE_NAME"));
}

DBeaver Setup

To use OxiDB in DBeaver:

  1. Download oxidb-jdbc-0.1.0.jar
  2. Open DBeaver → DatabaseDriver ManagerNew
  3. Fill in:
    • Driver Name: OxiDB
    • Class Name: com.oxidb.jdbc.OxiDbDriver
    • URL Template: jdbc:oxidb://{host}:{port}
    • Default Port: 4444
  4. Go to Libraries tab → Add File → select the JAR
  5. Click OK
  6. Create a new connection using the OxiDB driver with host 127.0.0.1 and port 4444

Once connected, you can browse collections as tables, inspect document fields as columns, and run SQL queries directly in DBeaver's SQL editor.

How It Works Internally

SQL Execution Flow

When you call stmt.executeQuery("SELECT * FROM users"), here's what happens:

  1. Build command map: {"cmd": "sql", "query": "SELECT * FROM users"}
  2. Encode to OxiWire: The map is encoded as [0xDB][0x08][count=2]["cmd"][0x06"sql"]["query"][0x06"SELECT..."]
  3. Frame and send: The encoded bytes are wrapped in a 4-byte LE length prefix and sent over TCP
  4. Receive response: Read 4-byte length, then payload. Decode: [0xDB][0x00=ok][0x07 array][documents...]
  5. Build ResultSet: Each document map becomes a row. Union of all keys becomes column names.

Type Mapping

OxiWire types map to JDBC types as follows:

OxiWire TypeJDBC TypeJava Class
Null (0x00)NULLnull
Boolean (0x01/0x02)BOOLEANjava.lang.Boolean
Int64 (0x03)BIGINTjava.lang.Long
Float64 (0x05)DOUBLEjava.lang.Double
String (0x06)VARCHARjava.lang.String
Array (0x07)ARRAYjava.util.List
Map (0x08)JAVA_OBJECTjava.util.Map

Column Discovery

Since OxiDB is a document database, collections don't have a fixed schema. The JDBC driver handles this by:

  • ResultSetMetaData: Scans all rows in the result set, collects the union of all field names, and infers types from the first non-null value per column
  • DatabaseMetaData.getColumns(): Samples one document from each collection via SELECT * FROM table LIMIT 1 and reports its fields as columns
  • Primary key: Always reports _id as the primary key for every collection (OxiDB's auto-generated document ID)

Download

Download oxidb-jdbc-0.1.0.jar (38 KB)

# Or via curl
curl -LO https://oxidb.baltavista.com/releases/v0.18.0/oxidb-jdbc-0.1.0.jar

# Maven (coming soon)
<dependency>
  <groupId>com.oxidb</groupId>
  <artifactId>oxidb-jdbc</artifactId>
  <version>0.1.0</version>
</dependency>

The driver is 38 KB, has zero external dependencies, requires Java 17+, and supports the full OxiDB SQL dialect including SELECT, INSERT, UPDATE, DELETE, CREATE TABLE, CREATE INDEX, and aggregation queries.