Tentackle Database — The Low-Level Persistence Engine¶
Overview and Motivation¶
The tentackle-database module (Java package org.tentackle.dbms) is Tentackle's low-level persistence
engine — the runtime that actually moves data between Java objects and a relational database. It sits between
the dialect-aware tentackle-sql backend below it and the
PDO-aware tentackle-persistence layer above
it, and it provides the concrete implementation of the Session
abstraction.
Its two central classes are:
Db— a persistence session: the implementation ofSessionthat owns transactions, connections, prepared statements, id-sources, and statistics.AbstractDbObject— a persistent low-level database object: the base class every persistence implementation extends, providing generic insert/update/delete/select machinery driven by entity-specific code that the Wurbelizer generates from the model.
The defining feature of this layer is 2-tier / n-tier transparency. A Db session is an abstraction over a
connection to a "server", where server need not be a database server at all:
- A local session talks to a database backend through a
ConnectionManagerthat owns the physical JDBC connection. This is what client apps in 2-tier mode and application servers use. - A remote session is connected to a Tentackle application server over TRIP. This is what n-tier (n ≥ 3) clients use.
Because both look identical to the code above, a Tentackle application switches between 2-tier and n-tier deployment by changing a config file, not the source code.
Design principles¶
- Session over connection, not session = connection. A
Dbdoes not own a JDBC connection directly; it asks aConnectionManagerto attach one for the duration of a task and detach it afterward. This decoupling is what enables connection multiplexing in servers. - Generated specialization, generic engine.
AbstractDbObject/AbstractDbOperationhold all the generic persistence logic; the per-entity SQL and column mapping are generated (by wurblets) into the application's subclasses. The OO "template method" approach keeps the framework code entity-agnostic. - Location transparency via TRIP delegates. Every persistence capability has a remote-delegate twin in
org.tentackle.dbms.trip, so the same calls run locally or against a remote server. - SQLExceptions never leak. Statement/result wrappers and the session utilities translate vendor
SQLExceptions into Tentackle'sPersistenceExceptionhierarchy, classified via theBackend, so upper layers never see raw JDBC errors. - Replaceable singletons via SPI. Utilities, factories, and the modification tracker are
@Service-registered so the PDO layer above can swap in PDO-aware versions transparently.
Key Concepts¶
Db — the session¶
Db implements Session (from
tentackle-session) and is the workhorse of the module.
Each thread must use its own Db. Its responsibilities include:
| Group | Examples | Purpose |
|---|---|---|
| Connection brokering | getConnectionManager, attach/detach (internal), isRemote |
Acquire/release a managed connection for a unit of work. |
| Transactions | begin, commit, rollback, createTransaction, savepoints |
Run (nestable) transactions; the voucher protocol from Session decides who really commits. |
| Statements | getPreparedStatement, createStatement |
Obtain cached, wrapped statements bound to the session's connection. |
| Id sources | the per-session IdSource (ObjectId / ObjectSequenceId) |
Allocate unique object IDs. |
| Batching | executeBatch, batch configuration |
Collect prepared statements into JDBC batches for throughput. |
| Modification tracking | the session's ModificationTracker integration |
Advance table serials on writes for caches/notifications. |
| Diagnostics | statement history, statement/transaction statistics, the statement tracer | Observe and profile database activity. |
| Keep-alive / lifecycle | isAlive, reconnection policy |
Survive idle timeouts and dropped connections. |
A Db can be cloned and pooled, and exposes whether it is local or remote so the rest of the framework
can adapt where it genuinely matters (rarely).
AbstractDbObject and AbstractDbOperation¶
AbstractDbObject<P> is the base of all persistent objects.
It is associated with a Session (local or remote) and provides the generic CRUD engine; entity specifics
(table/column mapping, finders, relations) are generated by wurblets into subclasses according to the
model. It carries the bookkeeping every
persistent row needs — object id, serial (optimistic locking), table serial (cache expiry) — and is
@TripSerializable so instances can travel between JVMs.
AbstractDbOperation<P> is the counterpart for operations
— units of work that are not a single row but a (often complex, transactional) procedure bound to a Db session,
and are likewise remoting capable.
Per-class static metadata lives in DbObjectClassVariables
and DbOperationClassVariables, created via the
DbClassVariablesFactory (which the PDO layer replaces
with a PDO-aware factory).
Connection management¶
JDBC connections are never used directly. A session borrows one from a ConnectionManager via attach, uses
it for a transaction/one-shot, then detaches it — letting the manager broker N sessions over M connections.
ConnectionManager— the broker interface (login/logout,attach/detach/forceDetach,getMaxConnections, …).DefaultConnectionManager— strict 1:1 mapping (each session keeps its own physical connection).MpxConnectionManager— multiplexing manager that maps N sessions onto M < N connections, transparently to the application; the typical choice for application servers with many clients. (Note: this is not session pooling — multiplexing is invisible to the app.)ManagedConnection— a JDBC connection wrapped with a prepared-statement cache andSQLException→PersistenceExceptiontranslation.ManagedConnectionMonitor— watches managed connections;DefaultReconnectionPolicydecides how to reconnect after a failure.
Connection managers are not used by remote sessions (those go over TRIP instead).
Session creation and pooling¶
DbFactory(@Service(SessionFactory.class)) — createsDbsessions; it is the local implementation of the session factory contract.DbPool— aSessionPoolofDbs for oneSessionInfo. With the default connection manager each pooledDbis one physical connection; with the multiplexing manager pooledDbs are virtual connections attached only during operations — the preferred server configuration.MultiUserDbPool— a pool ofDbPools, one perSessionInfo, for multi-user servers.DbRemoteSessionFactory(@Service(RemoteSessionFactory.class)) andRemoteSessionAdapterprovide the remote-session side.
Id sources¶
IdSource supplies unique object IDs (normally one instance per
session). Two implementations ship:
ObjectId— used when the backend has no sequences; updates a counter table, optimized to write only once at the end of transaction.ObjectSequenceId— uses a database sequence, multiplied by a configurable factor with an in-range counter to cut round-trips.
AbstractIdSource is the shared base; selection/configuration
goes through IdSourceConfigurator /
DefaultIdSourceConfigurator.
Statements, queries and result sets¶
StatementWrapper/PreparedStatementWrapper— wrap JDBC statements, catch/translateSQLExceptions, and guard against accidental reuse.ResultSetWrapper— anAutoCloseablewrapper over aResultSet;ResultSetSection/ResultSetSkipBlocksupport joines (due to inheritance or for load joins).Query— builds the SQL string + parameter set for a one-off query (e.g., user-entered), adding the backend-specificSELECT/LIMIT/OFFSETautomatically.DbBatch/DbBatchStatement— JDBC batching of standard prepared statements, grouped per root-entity type to preserve referential integrity, flushed at savepoints/commit. Enabled via thebatchsizebackend property; off by default and to be used with care because it complicates error diagnosis.
Transactions¶
AbstractTransaction is the serializable base;
DbTransaction holds local transaction state and
RemoteTransaction the remote variant.
DbTransactionFactory creates them and
DbTransactionHandle is the caller's handle.
TransactionStatistics collects per-transaction metrics.
Modification tracking and table serials¶
This layer implements the change-tracking contracts from tentackle-session:
DbModificationTracker(@Service(ModificationTracker.class)) — the concrete tracker; advances serials when data changes and notifies listeners (e.g., caches).DbModification(tablemodification) andModificationTally— the persisted per-table modification serial and its in-memory counter (local sessions only).TableSerialHistoryandTableSerialExpirationBacklog— track committed inserts/updates/deletes in memory so caches can be expired precisely, filling the gaps left by deletions and rolled-back changes and avoiding needless cache invalidation. Maintained only by the server connected to the database; intermediate servers and clients fetch their expiration sets remotely.DbModificationType/ModificationType/ModificationTypeFactory— enumerate INSERT/UPDATE/DELETE (and friends).
Modification logging¶
ModificationLog (table modlog) records object modifications
for asynchronous database coupling, higher-level replication, audit, etc. Objects opt in via
ModificationLoggable; logs are produced through
ModificationLogFactory /
DefaultModificationLogFactory.
Persistence visitors¶
PersistenceVisitor is a reflective hook registered on a
Db (valid for one transaction) that is consulted before each INSERT/UPDATE/DELETE to decide whether the
operation is allowed or should be silently skipped — e.g.
IgnoreDuplicatesPersistenceVisitor.
Diagnostics and statistics¶
StatementTracer— on-demand SQL tracing (off by default for performance), withStatementTraceException.StatementStatistics/StatementStatisticsResultandStatementHistory— collect and report statement usage.DbDiagnosticUtilities(@Service(DiagnosticUtilities.class)) — adds persistence info to thread stack dumps.
Utilities (SPI replacements)¶
DbUtilities(@Service(DbUtilities.class)) — persistence helpers; the clean seam between the low- and high-level layers (the PDO layer replaces it withPersistenceUtilities).DbSessionUtilities(@Service(SessionUtilities.class)) — extends the session utilities to mapSQLException→PersistenceException.
Database-backed Preferences¶
The org.tentackle.dbms.prefs package implements the standard java.util.prefs.Preferences API with the
database as the backing store, so application preferences are shared across all JVMs rather than living in a
local registry/file:
DbPreferences— thePreferencesimplementation; nodes persist viaDbPreferencesNodeand key/value pairs viaDbPreferencesKey; changes are flushed throughDbPreferencesOperationand node/key events propagate to all JVMs.DbPreferencesFactory(@Service(PersistedPreferencesFactory.class)) — a drop-in replacement for the JRE preferences factory, normally reached viaPersistedPreferencesFactory.getInstance().prefs/trip— the remote delegate so preferences work over a remote connection.
Remoting (TRIP)¶
The org.tentackle.dbms.trip package makes the whole layer location transparent. The connection/handshake
chain is:
TripServer— the generic application server; parses theservice=<uri>backend properties and creates the TRIP transports that accept client connections.RemoteDbConnection— the first remote object handed to a connecting client; handles login to a session.RemoteDbSession— the server-side session that creates all other delegates for the client; cleaned up byRemoteDbSessionCleanupThread.
Each capability is exposed as a remote delegate (interface + …Impl executor):
| Delegate / impl | Mirrors |
|---|---|
DbRemoteDelegate / …Impl |
Db (the session itself) |
AbstractDbObjectRemoteDelegate / …Impl |
AbstractDbObject CRUD/queries |
AbstractDbOperationRemoteDelegate / …Impl |
AbstractDbOperation |
DbModificationTrackerRemoteDelegate / …Impl |
modification tracking / serials |
ModificationLogRemoteDelegate / …Impl |
modification logs |
RemoteDbDelegateLocator /
DbRemoteDelegateLocator
(@Service(RemoteDbDelegateLocator.class)) find the right delegate for a class; the invocation handlers
(DbRemoteInvocationHandler,
RemoteDbDelegateInvocationHandler) and
RemoteDbSessionInvocationFilter route and
guard the calls. SQLExceptionComponentProvider
/ SQLExceptionInstanceCreator ensure JDBC
exceptions serialize correctly across the wire, and DefaultMasterSerial
carries the master modification serial back to clients.
Package Layout¶
| Package | Contents |
|---|---|
org.tentackle.dbms |
The engine: Db, AbstractDbObject/AbstractDbOperation, connection managers and ManagedConnection, pools and factories, id-sources, statement/query/result wrappers, transactions, batching, modification tracking + table serials, modification logging, persistence visitors, diagnostics/statistics and the SPI utility replacements. |
org.tentackle.dbms.prefs (+ prefs.trip) |
Database-backed java.util.prefs.Preferences and its remoting. |
org.tentackle.dbms.trip |
The TRIP server, remote connection/session and the remote delegates that make the layer location-transparent. |
org.tentackle.dbms.service |
The Hook (ModuleHook) for i18n bundle resolution. |
How It Fits Together¶
A persistence operation flows through the layers as follows:
tentackle-persistence (AbstractPersistentObject — PDO-aware)
│ extends
▼
AbstractDbObject ──── runs against ──── Db (Session)
│
local ────────────┤──────────── remote
▼ │ ▼
ConnectionManager │ DbRemoteDelegate (TRIP)
│ │ ▼ (on server)
ManagedConnection │ …RemoteDelegateImpl runs the
│ │ same AbstractDbObject logic
Backend (tentackle-sql)│ against a local Db
│ │ │
JDBC ◄───────────┘ ▼
results via TRIP
For a local Db, AbstractDbObject builds SQL via the
Backend, borrows a ManagedConnection from the
ConnectionManager, executes through the statement wrappers, and maps the ResultSetWrapper back to objects. For a
remote Db, the call is routed by the delegate locator to a …RemoteDelegate; on the server the matching
…RemoteDelegateImpl runs the very same AbstractDbObject logic against its own local Db and returns the
results over TRIP. Either way every write advances a table serial in the DbModificationTracker, which caches and
remote clients consult (via the expiration backlog) to invalidate exactly the stale entries.
The per-entity classes that extend AbstractDbObject/AbstractDbOperation are generated by wurblets from the
model; this module is the generic runtime they
plug into.
Module Dependencies¶
tentackle-database sits in the middle of the persistence stack:
- It requires transitive
tentackle-session(theSessioncontract it implements) andtentackle-sql(the dialect-awareBackendit executes), plusjava.naming. - It bundles the PostgreSQL JDBC driver for tests; production drivers are supplied by the application.
- It opens
org.tentackle.dbmsandorg.tentackle.dbms.prefstoorg.tentackle.corefor TRIP serialization. - It provides a
ModuleHookservice (org.tentackle.dbms.service.Hook) for i18n. - Through
@Service, it replaces several lower-layer singletons (SessionFactory→DbFactory,ModificationTracker→DbModificationTracker,SessionUtilities→DbSessionUtilities,DiagnosticUtilities→DbDiagnosticUtilities,PersistedPreferencesFactory→DbPreferencesFactory); the PDO layer above in turn replacesDbUtilities/DbClassVariablesFactorywith PDO-aware versions.
Related Documentation¶
- Session — the
Session/transaction/tracking contracts this module implements. - Tentackle SQL — the dialect-aware backend that builds the executed SQL.
- Persistence — the PDO-aware layer that builds on
AbstractDbObject/Db. - PDO — the persistent domain object pattern at the top of the stack.
- TRIP — the remoting protocol behind the
*.tripdelegates. - Model Definition — where the entities whose persistence code extends this module are declared.
- Services / ServiceFinder — the SPI mechanism behind the
@Service-replaced singletons and delegate discovery.