Mortar is Java-first, refactor-safe, SQL-transparent query authoring for Spring and PostgreSQL.
Mortar keeps query construction in Java while making the SQL visible before it executes. It is not an ORM: it does not provide managed entity state, lazy loading, identity maps, implicit graph traversal, or repository method-name query derivation.
Status: alpha. Java artifacts are published as 0.1.0-alpha.1. Rust crates
and the VS Code extension are published as 0.1.0. Mortar remains pre-1.0;
APIs may still change, and production adoption should pin exact versions and
review migration notes.
- Java 21 and Spring Boot 3.5.x.
- PostgreSQL 16 through a dedicated PostgreSQL renderer.
- Generated Java metamodels for annotated models.
- Generated fixed reads:
findByIdand explicitfindAll. - Java DSL reads with predicates, joins, projections, sorting, and pagination.
- Scalar
countandexiststhrough DSL scalar terminals. - Insert, update, delete, row-count mutations, and PostgreSQL
RETURNING. - JDBC runtime execution through explicit
MortarJdbcClientcalls. - Spring Boot starter auto-configuration for PostgreSQL/JDBC wiring.
- SQL assertions, snapshots, metadata, CLI, LSP, and VS Code SQL visibility.
Mortar currently does not claim broad dialect support, broad ORM replacement, application migration compatibility, or performance superiority over JDBC, PostgreSQL, jOOQ, QueryDSL, Hibernate, or Spring Data.
java/core Pure query model and DSL contracts
java/processor Annotation processor and generated metamodels
java/dialect-postgres PostgreSQL rendering
java/runtime-jdbc JDBC execution adapter
java/spring-boot-starter Spring Boot integration
java/testkit SQL assertions and snapshot helpers
editors/vscode VS Code client for Mortar LSP
editors/intellij IntelliJ plugin adapter
rust/crates/mortar-cli CLI tooling
rust/crates/mortar-compiler Metadata, diagnostics, and snapshot tooling
rust/crates/mortar-lsp Language Server Protocol server
examples/spring-boot-postgres
examples/clean-architecture-postgres
- Getting started
- Usage guide
- Query recipes
- API reference
- Spring Boot PostgreSQL example
- DDD and Clean Architecture example
- Query safety and refactors
- SQL snapshots
- Testkit
- LSP
- VS Code
- Build metadata
- Performance
- Benchmarks
- Comparison
- Troubleshooting
- Release policy
- Roadmap
- Current release plan
- Architecture decisions
- Contributing
- Security
The repository includes compile-backed examples for the current alpha:
examples/spring-boot-postgresexamples/clean-architecture-postgresexamples/query-corpus-*
The official public backend demo is
sequelcore/sequel-backend-demo.
It is a standalone Spring Boot backend that consumes Mortar from Maven Central
and starts with Mortar-only repository adapters. Other Sequel libraries should
be added there only after they are release-ready.
Java/Spring artifacts:
dependencies {
implementation("io.github.sequelcore:mortar-core:0.1.0-alpha.1")
implementation("io.github.sequelcore:mortar-dialect-postgres:0.1.0-alpha.1")
implementation("io.github.sequelcore:mortar-runtime-jdbc:0.1.0-alpha.1")
implementation("io.github.sequelcore:mortar-spring-boot-starter:0.1.0-alpha.1")
annotationProcessor("io.github.sequelcore:mortar-processor:0.1.0-alpha.1")
testImplementation("io.github.sequelcore:mortar-testkit:0.1.0-alpha.1")
}Rust tooling crates:
cargo install sequel-mortar-cli --version 0.1.0
cargo install sequel-mortar-lsp --version 0.1.0VS Code extension:
Install sequelcore.mortar-vscode from the VS Code Marketplace using the
pre-release channel.
The extension is a lightweight client. It does not bundle the Rust executables;
install sequel-mortar-cli and sequel-mortar-lsp separately and keep
mortar and mortar-lsp on PATH, or configure their paths explicitly.
Configure the Spring Boot starter for PostgreSQL:
mortar.dialect=postgresIf the Rust tools are not on PATH, configure VS Code explicitly:
{
"mortar.lsp.path": "mortar-lsp",
"mortar.cli.path": "mortar",
"mortar.postgres.connection": "postgres://user:password@localhost:5432/app"
}Model classes are annotated so the processor can generate Q* metamodels:
@MortarEntity(table = "clients")
public record Client(
@MortarId @MortarColumn("id") Long id,
@MortarColumn("name") String name,
@MortarColumn("active") boolean active
) {}Repository adapters build inspectable query values and execute them explicitly. Common primary-key reads use the generated fixed-read facade:
MortarBoundQuery<QClient.FindByIdRow> query = QClient.CLIENT
.read(renderer)
.findById(clientId)
.named("ClientRepository.findById");
Optional<QClient.FindByIdRow> row = jdbcClient.fetchOptional(query);Explicit full-table reads are generated too, and should be reserved for small reference data or bounded fixtures:
List<QClient.FindAllRow> rows = jdbcClient.fetch(
QClient.CLIENT
.read(renderer)
.findAll()
.named("ClientRepository.findAll")
);Application-specific reads use the Java DSL for predicates, projections, sorting, and pagination:
QuerySpec query = db.from(CLIENT)
.projectRecord(ClientSummary.class, client -> client.id, client -> client.name)
.where(client -> client.active.eq(true))
.orderBy(client -> client.id.asc())
.page(MortarPage.of(page, size))
.named("ClientRepository.findActivePage")
.build();Scalar reads use DSL terminals:
long activeCount = jdbcClient.fetchOne(
db.from(CLIENT)
.where(client -> client.active.eq(true))
.count(renderer)
.named("ClientRepository.countActive")
);
boolean exists = jdbcClient.fetchOne(
db.from(CLIENT)
.where(client -> client.id.eq(clientId))
.exists(renderer)
.named("ClientRepository.existsById")
);Writes use explicit mutation specs. Non-returning mutations return row counts:
MortarBoundMutation deactivate = MortarBoundMutation.unnamed(
new UpdateSpec(
CLIENT.table,
List.of(Assignment.of(CLIENT.active, false)),
List.of(CLIENT.id.eq(clientId)),
List.of()
),
renderer
)
.named("ClientRepository.deactivate");
int changedRows = jdbcClient.execute(deactivate);PostgreSQL RETURNING uses a returning mutation value:
MortarReturningMutation<ClientSummary> create = MortarReturningMutation.unnamed(
new InsertSpec(
CLIENT.table,
List.of(
Assignment.of(CLIENT.id, clientId),
Assignment.of(CLIENT.name, name),
Assignment.of(CLIENT.active, true)
),
List.of(CLIENT.id, CLIENT.name)
),
renderer,
ClientSummary.class
)
.named("ClientRepository.create");
ClientSummary created = jdbcClient.fetchOptional(create).orElseThrow();Adapter tests assert the generated SQL before runtime:
assertThatSql(query)
.hasSql("select c.id, c.name, c.active from clients c where c.id = ?")
.hasParameters(clientId)
.hasParameterTypes(Long.class);The current first-user path is:
- Run
gradlew.bat :examples:spring-boot-postgres:test. - Read
docs/getting-started.md. - Copy repository shapes from
docs/query-recipes.md. - Use
docs/usage-guide.mdfor DSL reads, scalars, mutations, Spring Boot wiring, and Clean Architecture placement.
gradlew.bat check --no-daemon
cd rust
cargo fmt --all --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cd ../editors/vscode
bun run typecheck