Skip to content

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 via isTracked(), getContextAttribute(), etc.
  • Persistability guardsassertEntityIsPersistable() and assertEntityNotEmbedded() 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() (the WHERE tree), getExtraArguments() (attributes to update), getSortingArguments()/getDefaultSorting() (ordering), and getJoinPaths()/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), and createJdbcSetterName() / createJdbcGetterName() (pick the right ResultSet/PreparedStatement accessor for a DataType).

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 base ModelWurblet, 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-model and tentackle-sql for the model and DataType/Backend APIs.

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.