mansart-jakarta-data implements Jakarta Data 1.0 — @Repository annotations, query methods, pagination, JDQL — on top of JDBC, with no heavy ORM, no runtime reflection. Every user-defined repository is materialized at compile time by APT. Official TCK: 73/73 PASS (M7, May 2026).
Mission
-
Cover the common persistence needs (CRUD, typed queries, pagination) without forcing an
EntityManager. -
Zero runtime reflection — static metamodel + cached
MethodHandle. -
Pluggable JDBC dialects (H2, PostgreSQL delivered; MariaDB / SQL Server on the backlog).
-
AOT-compatible (GraalVM native-image, Leyden CDS).
Modules
| Sub-module | Role |
|---|---|
|
|
|
Runtime: |
|
APT — static metamodel ( |
|
Maven plugin — generates, at build time, the |
|
SPI: |
|
H2 dialect (test reference + embedded). |
|
PostgreSQL dialect (production target). |
|
CDI 4.1 bootstrap — BCE that reads |
|
H2 in-memory unit tests. |
|
Jakarta Data 1.0 TCK runner — out of reactor (standalone POM Model 4.0.0). |
Quickstart
See Getting started. Summary:
@Entity
public class Author {
@Id @GeneratedValue Long id;
String name;
}
@Repository
public interface AuthorRepository extends BasicRepository<Author, Long> {
List<Author> findByName(String name);
@Query("FROM Author WHERE name LIKE :pattern")
List<Author> search(String pattern);
}
// APT generates AuthorRepositoryImpl + _Author + META-INF/mansart-repositories.list
Functional scope (delivered)
-
BasicRepository,CrudRepository,DataRepository. -
Inherited methods:
save,delete,findById,findAll,count,existsById. -
Lifecycle:
@Insert,@Update,@Delete,@Save. -
@Find(parameter-name binding). -
Derived queries:
findBy,existsBy,countBy,deleteBy+And,Or,Like,Between,LessThan,GreaterThan,LessThanEqual,GreaterThanEqual,IgnoreCase,OrderBy<Asc|Desc>. -
@QueryJDQL (SELECT/UPDATE/DELETE). -
Offset pagination (
PageRequest.ofPage(n).size(s)) and keyset (PageRequest.afterKey(…)). -
Sort,Order,Limit. -
SQLState → Jakarta Data exception mapping (
OptimisticLockingFailureException,EntityExistsException, etc.).
Out of scope v1
-
Explicit JDQL subqueries.
-
Multi-level explicit joins.
-
Multi-attribute aggregations.
-
@OneToMany,@ManyToMany,@MappedSuperclass,@Inheritance. -
@Convert/AttributeConverter. -
NoSQL (Jakarta Data NoSQL is an extension out of scope for Mansart v1).
Deferred to mansart-persistence (M7) or v1.1.
Runtime architecture
-
Query plans prepared at
<clinit>(static constants). -
MethodHandlecached once viaMethodHandles.privateLookupIn(…). -
Connection: borrowed from the injected
DataSource; released at the end of the method (unless a TX is active → the TX connection).
See Internals for codegen and mapping details.
Repositories from external jars (AOT generation, Maven plugin)
By default, only the @Repository interfaces present in the application’s sources are
materialized by the APT. A @Repository shipped by an already-compiled dependency (a library
that did not run the APT) therefore has no implementation: at runtime the BCE falls back to a
reflective path (a proxy generated at startup via the Class-File API).
mansart-data-maven-plugin removes that fallback for application builds. Its
generate-external-repositories goal (phase generate-sources):
-
scans the compile classpath (JDK Class-File API) for
@Repositoryinterfaces with no implementation (deduplicated against themansart-repositories*.listindices already present); -
reuses the very same emitters as the APT through a source-less
javactask — no duplicated codegen; -
generates the
*RepositoryImpl(and any missing_Entitymetamodels) into an application-owned package, referring to the external types by fully-qualified name → no JPMS split package with the originating jar; -
writes the
META-INF/mansart-repositories-external.listindex, read by themansart-data-cdiBCE alongside the APT index.
The result: beans for library-provided repositories are wired statically (zero reflection), just like the application’s own. The reflective path remains the safety net for archives you do not build (TCK suites, opaque jars).
<plugin>
<groupId>io.vidocq.mansart</groupId>
<artifactId>mansart-data-maven-plugin</artifactId>
<version>${mansart.version}</version>
<executions>
<execution>
<goals><goal>generate-external-repositories</goal></goals>
</execution>
</executions>
<!-- Optional: target package for the generated classes (default: derived from project coordinates). -->
<configuration>
<targetPackage>com.acme.app.mansart.generated</targetPackage>
</configuration>
</plugin>
|
The generated metamodel reads the entity’s private fields via |
Dialects
| Dialect | Status | Key notes |
|---|---|---|
H2 |
✅ Delivered |
|
PostgreSQL |
⏳ M4 |
|
MariaDB |
❌ Backlog |
— |
SQL Server |
❌ Backlog |
— |
Key differences isolated in the dialect: pagination, upsert, identifier return, native types (UUID, JSONB, BOOLEAN), snake_case schema.
JPMS
module io.vidocq.mansart.data {
requires jakarta.data;
requires jakarta.persistence;
requires java.sql;
exports io.vidocq.mansart.data;
exports io.vidocq.mansart.data.runtime;
exports io.vidocq.mansart.data.dialect.spi;
uses io.vidocq.mansart.data.dialect.spi.DialectFactory;
}
Bench
First smoke: see lien:https://codeberg.org/Vidocq/mansart/src/branch/main/mansart-jakarta-data/BENCH.md[mansart-jakarta-data BENCH.md] (BENCH-20260504-01: findById H2 in-memory).
Bugs
See lien:https://codeberg.org/Vidocq/mansart/src/branch/main/mansart-jakarta-data/BUG.md[BUG.md]. Major historical bug: BUG-20260505-01 (TCK entities not metamodelled) — resolved via the M7 runtime repo creator.
Roadmap
-
✅ M2 — Skeleton + APT metamodel.
-
✅ M3a — H2 dialect + BasicRepository CRUD.
-
⏳ M3b — Keyset pagination, lifecycle, derived queries (extras).
-
⏳ M4 — PostgreSQL dialect.
-
⏳ M5 —
@QueryJDQL (extras — base delivered in M7-27). -
✅ M6 — Jakarta Data 1.0 TCK.
-
✅ M7 — Runtime impl generation — TCK 73/73 PASS.
See lien:https://codeberg.org/Vidocq/mansart/src/branch/main/mansart-jakarta-data/PLAN.md[PLAN.md] for sub-milestone details (M7-26 to M7-29).