SQL

Statements live in .sql files, and a comment block above each one says what to generate for it:

-- name: findTenant
-- returning: single
-- resultRowType: com.example.domain.Tenant
select id, slug, created_at
from tenant
where id = :id

name becomes the method, every :id becomes one of its arguments and the placeholder the driver binds, and returning decides whether you get one row, many, or a stream. Nothing says what :id is, because Tenant has an id component and the tenant table has an id column — either answers. Name a type only where neither can.

What you get back

A statement returns one of three things, depending on what you tell it:

You writeYou get
nothinga Map<String, Object> per row
resultRowType: com.example.domain.Tenanta Tenant, from a mapper written for you
resultRowConverter: com.example.ToTenantwhatever your own converter returns

returning then wraps that in Optional, List or Stream — or drops it for a write, which answers with the number of rows it changed.

Where to go next

SQL files — how a file is laid out, how several statements share one, and every key the front matter accepts.

Structure — how files and directories decide which repository a statement lands in, and how to override that.

Converters — how a row becomes something other than a Map. Naming a record is usually all it takes; there is a hand-written escape hatch for mappings a record cannot express, and the same types work for parameters on the way in.

Schema validation — holding your statements to the create table statements your project already keeps, so a column that does not exist fails the build rather than the request. It also settles what a parameter’s type is, which is most of the front matter a write statement used to need.

Transactions — running several statements on one connection, with plain JDBC or under a framework that opens the transaction for you.

Cookbook — whole statements for the queries that need more than a name and a return mode: IN lists, optional filters, pagination, streaming, nested records, enums, JSON columns, batches, and one statement per database.