Tentackle Persistence Wurblets — Implementation-Level Code Generation¶
Overview and Motivation¶
tentackle-persistence-wurblets is the build-time code generation module that produces the implementation
side of a Tentackle application's persistence: the executable SQL, the PreparedStatement plumbing, the caches,
the class variables, and the remote-delegate glue that make PDOs
actually load, store, update, and delete rows. It is the counterpart to
tentackle-wurblets, which generates the interface
side (constants, accessor declarations, DTOs, and model documentation). Both read the same application
model and the same
DataType/Backend definitions, so the interfaces, the
implementations, and the DDL produced by the
SQL Maven plugin all agree on
the schema.
Like its sibling, this module is built on the Wurbelizer engine: each
generator is a .wrbl template that the wurbelizer-maven-plugin compiles into a Java class and runs at build
time, weaving the generated text into guarded regions of the application's source files (the
//GEN-BEGIN:<tag> … //GEN-END:<tag> fold blocks that follow a // @wurblet <tag> <Name> anchor). The
developer keeps full control of the surrounding class; the wurblet owns only the fenced block and regenerates it on
every build.
Interface side vs. implementation side¶
tentackle-wurblets |
tentackle-persistence-wurblets (this module) |
|
|---|---|---|
| Generates into | PDO / operation interfaces, DTOs | PDO / operation implementation classes (and some interface anchors) |
| Typical output | CN_*/AN_* constants, getter/setter declarations, relation method declarations, unique-domain-key types |
field declarations, SELECT/INSERT/UPDATE/DELETE code, class variables, caches, remote delegates |
| Base wurblet class | ModelWurblet |
DbModelWurblet (extends ModelWurblet) |
Because this module depends on tentackle-wurblets, every helper available to the interface wurblets is also
available here, extended with persistence-specific machinery — most importantly the translation of the
wurblet-argument query language
into real SQL WHERE/ORDER BY/join clauses and the JDBC parameter binding that goes with it.
Why generate the persistence implementation?¶
A Tentackle entity's persistence implementation is almost entirely mechanical but highly repetitive and
error-prone if written by hand: for every query you need a SELECT string assembled from the right column-name
constants, a PreparedStatement whose parameters are bound in the correct order with the correct
DataType setter, a cached statement id, dialect-aware
paging/locking, aggregate component loading, and — when remoting is enabled — a matching remote delegate. Deriving
all of this from the model guarantees it stays correct as the model evolves, supports every target
backend at once, and frees the developer to write only the
genuinely custom logic outside the guarded regions.
Design principles¶
- Model-driven and backend-aware. Every statement is built from the model and the active backends; the same template emits correct SQL for PostgreSQL, Oracle, DB2, etc., and refuses to generate features a target backend doesn't support.
- Woven into the developer's classes. Generated implementation code lives in guarded regions, so a persistence class can freely mix generated queries with handwritten methods.
- Statement caching by construction. Generated queries register prepared-statement ids in the entity's class variables, so the runtime reuses statements without per-call string building.
- Transparent remoting. When an entity or method is remote-enabled, the wurblets generate both the local implementation and the TRIP remote-delegate code, so the same call works whether the JVM is directly connected to the database or talking to a server.
DbModelWurblet — the shared base¶
DbModelWurblet extends
ModelWurblet and is the
base class for every wurblet in this module (pulled in via
header.incl's @{extends DbModelWurblet}). On top of
the model loading and target resolution it inherits, it adds:
- Tracking and context options parsed in
run():--context=<key>(domain-context column),--tracked/--attracked/--fulltracked/--untracked(how modified state is tracked on the returned objects), exposed viaisTracked(),getContextAttribute(), etc. - Persistability guards —
assertEntityIsPersistable()andassertEntityNotEmbedded()make a wurblet fail cleanly when it is applied to an entity that cannot be queried directly. - The query-expression model — after parsing the
wurblet arguments,
it exposes the parts of a query:
getExpression()/getExpressionArguments()(theWHEREtree),getExtraArguments()(attributes to update),getSortingArguments()/getDefaultSorting()(ordering), andgetJoinPaths()/isWithJoins()/isWithFilteredJoins()(eager-loading joins). - SQL and JDBC code builders used inside the templates:
createRelopSql()/createRelopCode()(turn a condition into SQL and into the Java that appends it),createWhereSetPars()/createJoinSetPars()(bind statement parameters in order),createOrderBy(),createJoins(),createStatementId(),buildMethodParameters()/buildInvocationParameters()(the generated method signatures and call sites), andcreateJdbcSetterName()/createJdbcGetterName()(pick the rightResultSet/PreparedStatementaccessor for aDataType).
Two @{config ...}@ flags adjust how a template's arguments are parsed: pathAllowed (the WHERE expression may
follow relation paths) and groupArgs (the update wurblets accept a second group of <values> after a |).
The Wurblets¶
All templates live under src/main/wurblets/org/tentackle/persist/wurblet and carry their own usage
documentation in their leading @{comment ...}@ block. They fall into four groups.
Structural — declarations, class variables, and accessors¶
| Wurblet | Generates |
|---|---|
Declare |
Field declarations for the entity's attributes. Options: --protected, --annotations, --mock. |
ClassVariables |
The per-class PersistentObjectClassVariables holder (table alias, statement-id registry, super/sub-class wiring, eager relations) shared by all instances of the entity. |
MethodsImpl |
Getter/setter and other method implementations. Options: --noif (no interface ⇒ no @Override), --noudk, --noisroot, --mock. |
MethodCache |
The PdoMethodCache for an embedded entity. |
Query — select, update, delete, reference checks¶
These come in a Db… (low-level persistent object) flavor and a Pdo… (full PDO) flavor. All take a
wurblet-argument <expression>
and share the options --private, --append=<sqltext> and --classvar=<classvariables>; the Pdo… variants add
--noif.
| Wurblet | Generates | Notable extra options |
|---|---|---|
DbSelectList / PdoSelectList |
A method selecting a list of objects. | --sort, --lock, --limit, --offset, --resultset, --bounded; PDO adds --cursor, --immutable, --finallyImmutable, --mutable |
DbSelectUnique / PdoSelectUnique |
A method selecting a single object. | --sort, --lock; PDO adds --tokenlock, --immutable, --finallyImmutable, --mutable |
DbUpdateBy / PdoUpdateBy |
A bulk-update method. Takes <expression> | <values> (the groupArgs config). |
|
DbDeleteBy / PdoDeleteBy |
A bulk-delete method. | |
DbIsReferencing / PdoIsReferencing |
A method checking whether an object is referenced by others (referential-integrity guard). |
Relations and caching¶
| Wurblet | Generates |
|---|---|
PdoRelations |
The relation handling for an entity: loadComponents(), insertPlainWithComponents(), deletePlainWithComponents(), the referencing-class registration, and anchors for the per-relation PdoSelect…/PdoDelete…/PdoIsReferencing wurblets. Toggle blocks with --[no]loadcomponents, --[no]insertcomponents, --[no]deletecomponents, --nori, --noselects, --nodeletes, --novalidate, --mock. |
PdoCache |
A PdoCache for the entity. Options select the strategy (--strategy=PRELOAD\|LRU\|LFU\|FORGET), --maxsize, --keepquota, --mutable, --udk and additional unique (optionally filtered, method-extracted) indexes. |
Remoting¶
| Wurblet | Generates |
|---|---|
AssertRemote |
Verifies the entity's remote delegate interface/implementation exist, generating them from templates if missing (a no-op when remoting is disabled). |
RemoteMethod |
The remote-delegate invocation code for a method annotated @RemoteMethod. Options: --this (pass the PDO itself), --nocontext (omit the DomainContext), --instance=<object> (target object on the server). |
Shared template fragments¶
The .incl files factor out repeated generation logic shared by several query wurblets:
genwhere.incl (the WHERE clause, including the
class-id condition for single-table inheritance and the domain-context predicate),
genupdate.incl (the SET list of an update), and
gensetpar.incl (binding statement parameters,
including join and context parameters). header.incl is
the shared preamble with the imports and @{extends DbModelWurblet}.
Supporting Java Classes¶
| Class | Role |
|---|---|
DbModelWurblet |
Base class for all persistence wurblets (see above). |
WhereClauseGenerator |
A CodeGenerator that walks the parsed argument expression and emits the Java that builds the SQL WHERE clause. |
JoinClauseGenerator |
Emits the SQL for eager-loading joins, including filtered joins. |
RemoteIncludes |
Creates and updates the generated TRIP remote interface/implementation files (from FreeMarker templates) used by AssertRemote. |
RemoteMethodHelper |
Extracts the wurblet tags for a @RemoteMethod from the compiled class file. |
How It Fits Together¶
model (*.map / entity definitions) tentackle-sql (DataType, Backend)
│ │
└───────────────┬───────────────────────┘
▼
tentackle-wurblets ──► PDO / operation *interfaces*, DTOs,
(interface side) constants, unique-domain-key types
│
▼ (depends on)
tentackle-persistence-wurblets ──► PDO / operation *implementations*:
(this module, implementation side) field declarations, class variables,
SELECT/INSERT/UPDATE/DELETE, caches,
remote delegates (TRIP)
During an application build, the
Tentackle Maven plugin arranges for
the wurbelizer plugin to run both wurblet sets over the sources, in phases, so the interface anchors are filled
before the implementation anchors that reference them. A typical generated persistence class therefore contains a
Declare block, a ClassVariables block, a MethodsImpl block, a PdoRelations block (which in turn nests
PdoSelect…/PdoDelete… anchors), and — when the model marks the entity cacheable or remote — PdoCache and
AssertRemote/RemoteMethod blocks.
Module Dependencies¶
- Wurbelizer (
org.wurbelizer:wurbelizer) — the template engine and the weaving of guarded regions. tentackle-wurblets— the interface-side wurblets and their shared baseModelWurblet, the wurblet-argument parser and the model/SQL helpers this module builds upon.tentackle-build-support— shared build-time utilities (and FreeMarker template support for remote code).- Transitively,
tentackle-modelandtentackle-sqlfor the model andDataType/BackendAPIs.
Like tentackle-wurblets, this is a build-time only module, packaged with an Automatic-Module-Name of
org.tentackle.persist.wurblet; it runs on the wurbelizer plugin's classpath and is never shipped inside the
running application.
Related Documentation¶
- Tentackle Wurblets — the interface-side counterpart, the wurblet anchor mechanism, and the wurblet-argument query language.
PdoSelectList/PdoSelectUnique— generating finder methods, including eager-loading*load joins.- Eager Relations — model-level
select = eagervs. domain-level load joins, and why eager loading never cascades. - Model Definition — the model the wurblets consume.
- PDO — Persistent Domain Objects — the objects these implementations realize.
- Tentackle SQL —
DataType/Backendused to build the SQL and bind parameters. - TRIP — the remoting protocol behind the generated remote delegates.
- Tentackle Maven Plugin — drives wurblet execution during the build.
- Wurbelizer — How it works — the underlying code-generation engine.