Exposed DuckDB Adapter
Latest stable Based on Exposed release 1.11.0
bluetape4k-exposed-duckdb connects the Exposed JDBC DSL to embedded DuckDB. It registers a PostgreSQL-derived dialect and compensates for generated-key prepareStatement overloads that DuckDB JDBC does not implement.
Problem
Section titled “Problem”DuckDB is useful for local analytical SQL, but its embedded connection lifecycle and JDBC metadata surface differ from a server RDBMS. This adapter keeps those differences at the module boundary instead of scattering driver workarounds through repositories.
When to use it
Section titled “When to use it”Use it for in-process analytics, local files, repeatable data preparation, or tests that benefit from DuckDB SQL. Do not treat it as a drop-in replacement for a shared transactional service.
Coordinates
Section titled “Coordinates”dependencies { implementation(platform("io.github.bluetape4k:bluetape4k-dependencies:<version>")) implementation("io.github.bluetape4k.exposed:bluetape4k-exposed-duckdb")}Core concepts
Section titled “Core concepts”DuckDBDatabase.inMemory()creates a database private to each new connection.DuckDBDatabase.file(path)andreadOnly(path)open persistent or read-only files.DuckDBDialectreuses PostgreSQL SQL generation;DuckDBDialectMetadataskips unsupported imported-key metadata.queryFlowmaterializes rows inside the transaction before emitting them; it is not row-by-row streaming.
Quick start
Section titled “Quick start”val db = DuckDBDatabase.file("/tmp/events.duckdb")transaction(db) { SchemaUtils.create(Events) Events.insert { it[id] = 1L; it[region] = "kr" } val rows = Events.selectAll().limit(100).toList()}API by task
Section titled “API by task”| Task | API |
|---|---|
| Ephemeral database | DuckDBDatabase.inMemory() |
| Shared state across transactions | DuckDBDatabase.file(path) |
| Read-only analysis | DuckDBDatabase.readOnly(path) |
| Blocking JDBC on coroutines | suspendTransaction(db, dispatcher) |
| Flow consumption | queryFlow(db) { ... } |
Recommended patterns
Section titled “Recommended patterns”Use a file database or a single-connection pool when multiple transactions must observe the same state. Page or aggregate large datasets in SQL; queryFlow first loads the complete Iterable into memory. Supply Dispatchers.IO or a virtual-thread dispatcher for blocking JDBC.
Integrations
Section titled “Integrations”The DuckDB JDBC driver is an API dependency and no external service is required. Tests run directly against embedded DuckDB rather than Testcontainers. Exposed SchemaUtils, inserts, filters, grouping, ordering, and limit are covered by the release tests.
Configuration
Section titled “Configuration”Java 25+ test execution needs --enable-native-access=ALL-UNNAMED because the driver loads native code. Treat the database file as application data: choose its location, permissions, backup, and cleanup policy explicitly.
Failure modes
Section titled “Failure modes”- Separate in-memory connections see separate databases.
- Generated-key overload behavior is adapted, not made equivalent to every server driver.
- Imported-key discovery is unavailable, so do not use metadata inspection as proof that foreign keys exist.
- A large
queryFlowcan exhaust memory before its first item is emitted.
Operations
Section titled “Operations”Observe file size, query duration, memory pressure, and concurrent access. Keep read-only consumers on readOnly(path) and close application-owned pools or connections during shutdown.
Testing
Section titled “Testing”Prefer an isolated temporary file when a test spans connections; use inMemory() for a single connection scope. Test the generated SQL and the real DuckDB result, especially for PostgreSQL-derived syntax and nullable/time columns.
Workshops and learning path
Section titled “Workshops and learning path”Start with the database adapter matrix, then compare the workload in OLTP vs OLAP. Move to a server adapter only when shared availability or database-managed concurrency becomes part of the requirement.
Limitations
Section titled “Limitations”Release 1.11 does not provide row-streaming Flow, imported-key metadata, or a distributed service boundary. PostgreSQL dialect inheritance covers proven tests, not every PostgreSQL extension or DuckDB feature.
Release diagrams
Section titled “Release diagrams”These diagrams are loaded directly from README assets published with the 1.11.0 release and pinned to its immutable commit. They describe this manual’s released structure and runtime flows, not later Snapshot changes. Select a preview to open the SVG at the same release commit.
DuckDB Exposed integration boundary
Section titled “DuckDB Exposed integration boundary”Release README: exposed/duckdb/README.md
DuckDB query flow materialization
Section titled “DuckDB query flow materialization”Release README: exposed/duckdb/README.md

