Skip to main content

Getting Started

A step-by-step introduction to Foundations JDBC — from setup to executing queries. Foundations JDBC is MIT-licensed and open source.

Dependencies

Pick the one dependency for your language:

build.gradle.kts
implementation("dev.typr:foundations-jdbc:1.0.0-RC1")
pom.xml
<dependency>
<groupId>dev.typr</groupId>
<artifactId>foundations-jdbc</artifactId>
<version>1.0.0-RC1</version>
</dependency>

Each module includes everything you need — the Kotlin and Scala modules depend on the core transitively.

Imports

import dev.typr.foundations.*;           // Core: Fragment, RowCodec, Operation, Transactor, *Types
import dev.typr.foundations.connect.*; // Connection: SimpleDataSource, *Config
import dev.typr.foundations.data.*; // Data types: Json, Range, Uint4, etc.

Setting Up a Connection

The quickest way to get started is with DuckDB in-memory using SingleConnectionDataSource. DuckDB is an embedded database that requires no Docker, no server, and no setup — just add the dependency and go:

val tx: Transactor =
SingleConnectionDataSource.create(DuckDbConfig.inMemory().build())
.transactor()

val result: Int = sql { "SELECT 42" }
.queryExactlyOne(DuckDbTypes.integer)
.transact(tx)

For production connection setup with PostgreSQL, MariaDB, and other databases, see Transactors.

Your First Query

Define a record, a row codec that maps columns to fields, and a query that returns typed results:

data class City(val name: String, val population: Int)

val cityCodec: RowCodecNamed<City> =
RowCodec.namedBuilder<City>()
.field("name", DuckDbTypes.varchar, City::name)
.field("population", DuckDbTypes.integer, City::population)
.build(::City)

val findCities: Operation<List<City>> =
sql { "SELECT ${cityCodec.columnList} FROM city ORDER BY population DESC" }
.query(cityCodec.all())

The RowCodecNamed maps database columns to record fields by name. Use .all() to collect all rows, .exactlyOne() for a single result, or .maxOne() for an optional result. See Row Codecs for more.

Parameterized Queries

Use your types and codecs in a plain function to build parameterized queries:

fun findCityByName(name: String): Operation<City?> =
sql { "SELECT ${cityCodec.columnList} FROM city WHERE name = ${DuckDbTypes.varchar(name)}" }
.query(cityCodec.maxOne())

Values are always bound as JDBC parameters — never concatenated into SQL strings.

What's Next

Concepts Flow

Continue reading: FragmentsRow CodecsOperationsTransactorsQuery Analysis

Jump to a topic: Database Types (type catalog) · Templates (Advanced: reusable param holes)

Full Examples