Common recipes for mansart-jakarta-data, mansart-transactions, and mansart-pool. The exhaustive reference (all annotations, all configuration options) lives in Reference.
Jakarta Data repositories
BasicRepository<T, K> and CrudRepository<T, K>
BasicRepository covers save, saveAll, findById, findAll, deleteById, delete, deleteAll. CrudRepository adds insert/insertAll/update/updateAll. To go further, declare specialized methods on the interface; the APT turns them into static query plans.
@Repository
public interface AuthorRepository extends CrudRepository<Author, Long> {
long count();
boolean existsById(Long id);
}
Derived queries (by method name)
Mansart parses the name into a neutral AST, then produces SQL via the target dialect. Supported keywords: findBy, existsBy, countBy, deleteBy, operators And, Or, Like, Between, LessThan, GreaterThan, LessThanEqual, GreaterThanEqual, IgnoreCase, OrderBy<Asc|Desc>.
List<Author> findByName(String name);
Optional<Author> findOneByName(String name);
List<Author> findByNameLike(String pattern);
List<Author> findByNameIgnoreCase(String name);
List<Author> findAllByOrderByNameAsc();
long deleteByName(String name);
@Find (typed parameter matching)
@Find
List<Book> find(String title, Author author);
The Java parameter name must match the entity attribute (compile with -parameters); each parameter is an equality match. @Find methods are currently served by the reflective runtime fallback — range conditions (Between, LessThan, …) belong to derived method names such as findByPublishedOnBetween(from, to), which the APT compiles.
@Query JDQL
JDQL is the query dialect defined by Jakarta Data 1.0. Simpler than JPQL, sufficient for typed SELECT/UPDATE/DELETE.
@Query("FROM Author WHERE name LIKE :pattern AND id > :minId")
List<Author> search(String pattern, Long minId);
@Query("UPDATE Author SET name = :newName WHERE name = :oldName")
long rename(String oldName, String newName);
Pagination — offset and keyset
Page<Author> findByNameLikeOrderByNameAsc(String pattern, PageRequest page);
// Call
var page = authors.findByNameLikeOrderByNameAsc("S%", PageRequest.ofPage(1).size(20));
Keyset cursors (PageRequest.afterCursor(cursor)) avoid drift on long pagination.
Lifecycle annotations
@Insert, @Update, @Delete, @Save typed: parameter = entity, collection or varargs; return void/T/Iterable<T>/int/long/boolean. See Reference.
Transactions
@Transactional (CDI)
The mansart-transactions interceptor materializes @jakarta.transaction.Transactional (TxType REQUIRED, REQUIRES_NEW, MANDATORY, SUPPORTS, NEVER, NOT_SUPPORTED).
@ApplicationScoped
public class AuthorService {
@Inject AuthorRepository authors;
@Transactional
public Author register(String name) {
return authors.save(new Author(name));
}
@Transactional(Transactional.TxType.REQUIRES_NEW)
public void audit(String message) { /* ... */ }
}
UserTransaction (programmatic)
@Inject UserTransaction tx;
void run() throws Exception {
tx.begin();
try {
authors.save(new Author("Marguerite Yourcenar"));
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
}
Suspend / resume
TransactionManager.suspend() and resume(Transaction) cover batch / scheduler cases. The transactional context is carried in a ThreadLocal — a deliberate choice: the Jakarta Transactions API is imperative (begin() returns, commit() comes later from arbitrary call sites), which cannot fit ScopedValue’s enclosing-scope model; `ThreadLocal works on virtual threads exactly as on platform threads.
Connection pool
Minimal configuration
var ds = MansartDataSource.of(PoolConfig.builder()
.jdbcUrl("jdbc:postgresql://db.local:5432/shop")
.username("shop").password("...")
.minIdle(4).maxSize(32)
.acquireTimeout(Duration.ofSeconds(5))
.idleTimeout(Duration.ofMinutes(10))
.maxLifetime(Duration.ofMinutes(30))
.validation(ValidationMode.ON_BORROW)
.build());
Sizing
With virtual threads, the classic "threads = connections" sizing disappears: dimension on real DB concurrency, not application concurrency. Rule of thumb: maxSize ≈ DB vCPUs × 2..4. Beyond that, the DB becomes the bottleneck — see mansart-pool BENCH.md.
Named datasources (dataStore) in Vidocq NEW
In a Vidocq application, the pool is configured declaratively (the vidocq-runtime-mansart-pool-extension reads vidocq.pool. keys) and one application can talk to several databases. Names are *structural (fixed at build time), values are runtime MicroProfile Config.
Declare a named pool alongside the default one — the name is the single path segment after vidocq.pool.:
# @Default datasource
vidocq.pool.url=jdbc:postgresql://db.local:5432/shop
vidocq.pool.username=shop
vidocq.pool.maxSize=16
# named datasource "audit"
vidocq.pool.audit.url=jdbc:postgresql://db.local:5432/audit
vidocq.pool.audit.username=audit
vidocq.pool.audit.maxSize=4
Every key of PoolConfig has its property twin (minIdle, maxSize, acquireTimeout, idleTimeout, maxLifetime, validationTimeout, validation, validationQuery, leakDetectionThreshold), plus password, xa (XA enlistment) and xaDataSourceClass, for the default and for each name. Names can also be declared with @VidocqDataSources({"audit"}) on the application class; the two declaration paths are merged and deduplicated.
Route a repository to a named datasource with the standard Jakarta Data attribute:
@Repository(dataStore = "audit")
public interface AuditEntryRepository extends BasicRepository<AuditEntry, Long> { }
And inject the raw datasource anywhere with the generated @Named holder:
@Inject
@Named("audit")
DataSource auditDataSource;
Named datasources need the vidocq-runtime-mansart-pool-datasources-codegen jar on the compiler’s annotationProcessorPaths (it generates the @Named holder beans). The end-to-end walkthrough — including this wiring — is the REST + database tutorial; the runnable reference is vidocq-runtime-examples/vidocq-runtime-mansart-h2-example in the runtime repo.
Repositories routed via dataStore are built by the repository factory without interceptor enhancement; transactional semantics are handled, but custom CDI interceptors on such repositories are not applied today (tracked in the repo’s BUG.md).
|
In dev mode, the runtime’s PostgreSQL Dev Service can provision one disposable container per declared datasource name — nothing to install or configure. See DEV_SERVICES.md in the Vidocq runtime repo.
Composition with other modules
-
Vauban CDI — produces
@Repositorybeans, runs the@Transactionalinterceptor. -
Cassini REST — REST endpoints calling transactional services.
-
Vidocq Runtime — orchestrator that assembles pool + tx + data + REST into an AOT-friendly fat jar.