mansart-jakarta-data implémente Jakarta Data 1.0 — annotations @Repository, query methods, pagination, JDQL — au-dessus de JDBC, sans ORM lourd, sans réflexion runtime. Tout repository utilisateur est matérialisé à la compilation par APT. TCK officiel : 73/73 PASS (M7, mai 2026).

Mission

  • Couvrir les besoins courants de persistance (CRUD, queries typées, pagination) sans imposer un EntityManager.

  • Zéro réflexion à l’usage — métamodèle statique + MethodHandle cachés.

  • Dialectes JDBC pluggables (H2, PostgreSQL livrés ; MariaDB / SQL Server au backlog).

  • Compatible AOT (GraalVM native-image, Leyden CDS).

Modules

Sous-module Rôle

mansart-data-api

Annotations @Entity, @Repository, @Find, @Query, @Insert, @Update, @Delete, @Save, @Dialect, @JdbcRepository, @MansartDataSource.

mansart-data-core

Runtime : RepositoryRuntime, QueryPlan exécution, mapping ResultSet ↔ entité, gestion connexion.

mansart-data-processor

APT — métamodèle statique (Book, Book) + *RepositoryImpl + META-INF/mansart-repositories.list.

mansart-data-maven-plugin

Plugin Maven — génère à la compilation les *RepositoryImpl des @Repository issus de jars de dépendances (où l’APT n’a pas tourné), indexés dans META-INF/mansart-repositories-external.list.

mansart-data-dialect-spi

SPI : Dialect, DialectFactory, AST de requête neutre.

mansart-data-dialect-h2

Dialecte H2 (référence tests + embarqué).

mansart-data-dialect-postgresql

Dialecte PostgreSQL (cible production).

mansart-data-cdi

Bootstrap CDI 4.1 — BCE qui lit META-INF/mansart-repositories.list (et …-external.list produit par le plugin) et déclare les beans repository.

mansart-data-tests

Tests unitaires H2 in-memory.

mansart-data-tck

Runner TCK Jakarta Data 1.0 — hors reactor (POM Model 4.0.0 standalone).

Quickstart

Voir Démarrage rapide. Résumé :

@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 génère AuthorRepositoryImpl + _Author + META-INF/mansart-repositories.list

Périmètre fonctionnel (livré)

  • BasicRepository, CrudRepository, DataRepository.

  • Méthodes héritées : save, delete, findById, findAll, count, existsById.

  • Lifecycle : @Insert, @Update, @Delete, @Save.

  • @Find (binding par nom de paramètre).

  • Derived queries : findBy, existsBy, countBy, deleteBy + And, Or, Like, Between, LessThan, GreaterThan, LessThanEqual, GreaterThanEqual, IgnoreCase, OrderBy<Asc|Desc>.

  • @Query JDQL (SELECT/UPDATE/DELETE).

  • Pagination offset (PageRequest.ofPage(n).size(s)) et keyset (PageRequest.afterKey(…​)).

  • Sort, Order, Limit.

  • Mapping erreurs SQLState → exceptions Jakarta Data (OptimisticLockingFailureException, EntityExistsException, etc.).

Hors scope v1

  • Subqueries explicites en JDQL.

  • Joins explicites multi-niveaux.

  • Agrégations multi-attributs.

  • @OneToMany, @ManyToMany, @MappedSuperclass, @Inheritance.

  • @Convert / AttributeConverter.

  • NoSQL (Jakarta Data NoSQL est une extension hors scope Mansart v1).

Reportés à mansart-persistence (M7) ou v1.1.

Architecture runtime

Diagram
  • Plans de requête préparés à <clinit> (constants statiques).

  • MethodHandle cachés une seule fois via MethodHandles.privateLookupIn(…​).

  • Connexion : empruntée au DataSource injecté ; libérée à la fin de la méthode (sauf si TX active → connexion de la TX).

Voir Fonctionnement interne pour le détail du codegen et du mapping.

Repositories de jars externes (génération AOT, plugin Maven)

Par défaut, seuls les @Repository présents dans les sources de l’application sont matérialisés par l’APT. Une interface @Repository fournie par une dépendance déjà compilée (une bibliothèque qui n’a pas exécuté l’APT) n’a donc pas d’implémentation : à l’exécution, le BCE retombe alors sur un chemin réflexif (proxy généré au démarrage via la Class-File API).

mansart-data-maven-plugin supprime ce repli pour les builds applicatifs. Au goal generate-external-repositories (phase generate-sources), il :

  1. scanne le classpath de compilation (Class-File API du JDK) à la recherche des interfaces @Repository sans implémentation (déduplication via les index mansart-repositories*.list déjà présents) ;

  2. réutilise les mêmes émetteurs que l’APT via une tâche javac sans source — aucun codegen dupliqué ;

  3. génère les *RepositoryImpl (et les métamodèles _Entity manquants) dans un package possédé par l’application, en référençant les types externes par nom pleinement qualifié → pas de split-package JPMS avec le jar d’origine ;

  4. écrit l’index META-INF/mansart-repositories-external.list, lu par le BCE de mansart-data-cdi au même titre que l’index APT.

Résultat : les beans des repositories fournis par des bibliothèques sont câblés statiquement (zéro réflexion), exactement comme ceux définis dans l’application. Le chemin réflexif reste le filet de sécurité pour les archives qu’on ne build pas (suites TCK, jars opaques).

<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>
  <!-- Optionnel : package cible des classes générées (défaut : dérivé des coordonnées du projet). -->
  <configuration>
    <targetPackage>com.acme.app.mansart.generated</targetPackage>
  </configuration>
</plugin>

Le métamodèle généré lit les champs privés de l’entité via MethodHandles.privateLookupIn : une entité fournie par un jar externe doit donc rester accessible par réflexion (classpath, ou module ouvert). Une entité scellée sur le module-path nécessite un opens. Si la bibliothèque a elle-même été compilée avec l’APT (elle embarque déjà son _Entity), le plugin réutilise ce métamodèle et n’en régénère pas.

Dialectes

Dialecte Statut Notes clés

H2

✅ Livré

MERGE INTO, LIMIT/OFFSET, IDENTITY.

PostgreSQL

⏳ M4

ON CONFLICT, RETURNING, types UUID/JSONB.

MariaDB

❌ Backlog

SQL Server

❌ Backlog

Différences clés isolées dans le dialecte : pagination, upsert, retour d’identifiant, types natifs (UUID, JSONB, BOOLEAN), schéma snake_case.

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

Premier smoke : voir 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

Voir lien:https://codeberg.org/Vidocq/mansart/src/branch/main/mansart-jakarta-data/BUG.md[BUG.md]. Bug majeur historique : BUG-20260505-01 (entités TCK non métamodélisées) — résolu via le runtime repo creator de M7.

Roadmap

  • ✅ M2 — Squelette + APT métamodèle.

  • ✅ M3a — H2 dialect + BasicRepository CRUD.

  • ⏳ M3b — Pagination keyset, lifecycle, derived queries (compléments).

  • ⏳ M4 — Dialecte PostgreSQL.

  • ⏳ M5 — @Query JDQL (compléments — base livrée en M7-27).

  • ✅ M6 — TCK Jakarta Data 1.0.

  • ✅ M7 — Runtime impl generation — TCK 73/73 PASS.

Voir lien:https://codeberg.org/Vidocq/mansart/src/branch/main/mansart-jakarta-data/PLAN.md[PLAN.md] pour le détail des sous-jalons (M7-26 à M7-29).