This page describes what happens when the application calls authors.findByName("Sylvie Germain"): from the CDI proxy down to the pool, through JDQL → SQL translation and ResultSet → entity materialization. It is also the right place to understand why Mansart favors static codegen.
Overview — repository method call
The whole hot path is reflection-free: query plans are prepared once and cached, parameter/attribute binding goes through MethodHandle cached once.
Codegen — APT (compile time)
mansart-data-processor is a javax.annotation.processing.Processor that scans compiled CUs:
-
For each class annotated
@Entity(Mansart or JPA) → generatesBook.java(rich metamodel) andBook.java(JPA standard metamodel). -
For each interface annotated
@Repository→ generatesBookRepositoryImpl.java(final public implementation class) + an entry inMETA-INF/mansart-repositories.list.
Source code generated under target/generated-sources/annotations/, marked @Generated("io.vidocq.mansart.data.processor.MansartProcessor"). The processor has no runtime dependency — it lives only in the consuming module’s APT classpath.
Why APT and not runtime codegen?
-
AOT-friendly — GraalVM native-image, Leyden CDS: no proxy to register, everything is in the bytecode at build time.
-
Diagnosable — generated code is readable, debuggable, visible in stack traces.
-
Zero warmup — no dynamic classloading on first call.
-
strict Java Modules-friendly — generated classes are in the same package as sources, no need for
opens.
The Connection wrapper (hand-written)
mansart-pool ships a plain hand-written PooledConnection implements Connection: every method delegates to the physical connection, and close() returns the connection to the pool instead of closing it.
Why hand-written rather than java.lang.reflect.Proxy:
-
Connectionhas 50+ methods — a reflective proxy costs aninvokeper call; direct delegation is JIT-friendly from the first call. -
No bytecode generation and no ASM / Byte Buddy dependency — matches the ecosystem’s "zero external dependency" rule.
Pool acquire / release
-
Semaphore permits— hard pool bound, fair=false (perf). -
ConcurrentLinkedDeque idle— lock-free stack, push/pop at the head (hot LIFO). -
ConcurrentHashMap.newKeySet inUse— set of borrowed connections, for metrics and leak detection. -
Housekeeper virtual thread — periodic wake-up, evicts connections that are too old or idle, pre-warms up to
minIdle.
Details and numbers: see mansart-pool BENCH.md.
Threading — virtual threads
-
All blocking JDBC executions (
PreparedStatement.execute*,ResultSet.next) run on virtual threads. No platform pool. -
The JDBC connection is never held across a
synchronizedblock that wraps a blocking operation (anti-pattern: thread pinning). -
The transactional context lives in a
ThreadLocalon the transaction’s thread (see mansart-transactions); it does not auto-propagate intoStructuredTaskScopeforks — keep transactional work on the transaction’s thread. -
mansart-poolvalidates in CI with-Djdk.tracePinnedThreads=fullthat no pinning appears under 1000 parallel borrows.
ResultSet → entity mapping
No reflection. The _Book metamodel captures, in <clinit>, one MethodHandle setter per attribute, obtained via MethodHandles.privateLookupIn(Book.class, lookup). For each row:
Book row = constructor.invoke(); // MethodHandle constructor
_Book.title.setter.invokeExact(row, rs.getString(1));
_Book.author.setter.invokeExact(row, refLoader.get(rs.getLong(2)));
// ...
invokeExact on a MethodHandle is inlined by C2 — perf equivalent to direct field access after warmup.
Transactions — propagation and XA
-
Single-resource (the dominant case) — the
@Transactionalinterceptor opens/closes the transaction. The JDBC connection is enlisted as a degenerateXAResource(onePhase=true). -
Multi-resource (M4) — true 2PC:
preparethencommiton each enlistedXAResource. Recovery log (M5) recordsPREPAREDtransactions that did not complete, for recovery on reboot. -
Suspend / resume —
TransactionManager.suspend()removes the context from the current thread and returns it;resume(t)reinstates it. Typical batch case: a worker job suspending the caller’s TX to issue a clean INSERT/COMMIT.
See mansart-transactions PLAN.md for the M1-M8 roadmap.