Skip to content

Transaction Manager

KaumeiTxManager is a local JDBC transaction manager. It manages one JDBC connection for each active transaction and implements JdbcConnectionProvider so generated JDBC code can use that connection.

The manager state machine performs no annotation or method analysis and uses no reflection. It has no transaction-framework dependency. Transaction state is thread-local and no transaction context is held in shared static state. Jakarta Transactions naming and semantics are used where they fit the local JDBC model.

The manager does not provide:

  • XA, distributed transactions, or two-phase commit;
  • savepoints or nested transactions;
  • transaction timeouts;
  • asynchronous transactions or cross-thread context propagation;
  • interposed synchronisation;
  • user-defined interception, replacement, or wrapping of the physical JDBC commit or rollback operation;
  • implementation of the Jakarta Transactions API or its provider contract.
❗️Transaction context

Each active transaction context owns exactly one:

  • JDBC Connection;
  • thread binding;
  • rollbackOnly flag;
  • completion state: RUNNING, COMPLETING, or FINISHED;
  • optional terminal completion failure;
  • ordered list of KaumeiTxSynchronization callbacks.

Only the manager opens, commits, rolls back, and closes the transaction connection. A manager instance may serve multiple threads; their transaction contexts are isolated.

❗️Thread state

A manager has at most one current transaction on each thread.

Thread state Connection access Transaction operations
Active transaction Reuse its protected connection Rollback-only operations are allowed; synchronisation registration requires RUNNING
No active transaction Acquire a connection from the configured provider Operations requiring a transaction throw IllegalStateException

A connection acquired without a transaction uses its normal auto-commit behaviour. The caller owns that connection and connection reuse is not guaranteed. The absence of a transaction does not prohibit JDBC access.

Only the scope that started a transaction completes it automatically. Joined scopes participate in the same context and connection.

❗️Callback context

Every invoked transaction callback receives a non-null KaumeiTxContext. The context represents either the exact transaction started or joined for that invocation, or a no-transaction invocation. A no-transaction context is used when SUPPORTS, NOT_SUPPORTED, or NEVER invokes a callback without a transaction.

When a transaction-type precondition fails, the callback is not invoked and no callback context is exposed.

KaumeiTxContext.getConnection() returns only the protected connection of its active bound transaction. It never obtains a provider connection or starts a transaction. It throws IllegalStateException when no matching active transaction is bound and declares SQLException for JDBC connection access.

The remaining context operations have the same semantics as the corresponding manager operations. A no-transaction context reports false from isTransactionActive() and rejects every operation that requires a transaction.

A transaction-bound context may operate only while its exact transaction is current on the calling thread. Consequently:

  • it cannot operate on a replacement transaction or a transaction on another thread;
  • it is temporarily invalid while its transaction is suspended and becomes valid again when that transaction is restored;
  • a context from a joined callback remains valid after that callback returns while the same transaction remains current;
  • it becomes invalid after its transaction completes;
  • it throws IllegalStateException while another transaction or no transaction is current.

The context is transaction-bound rather than callback-lifetime-bound. Passing it to another thread does not propagate its transaction and is unsupported. A no-transaction context cannot affect a transaction that later becomes current on the thread.

❗️Protected connection

KaumeiTxManager.getConnection() depends only on the current thread state. It returns the current protected transaction connection when a transaction is active and otherwise delegates to the configured provider. It does not inspect annotations or methods. Participating JDBC code may therefore obtain the active connection without starting another transaction callback.

The protected connection prevents direct manipulation of manager-owned state. It rejects:

  • commit() and both rollback() forms;
  • setAutoCommit(...);
  • setTransactionIsolation(...);
  • setReadOnly(...);
  • both setSavepoint(...) forms and releaseSavepoint(...);
  • abort(...);
  • close().

The protected connection does not wrap or track statements, prepared statements, callable statements, or result sets.

unwrap(...) and isWrapperFor(...) may expose the underlying connection. The wrapper prevents accidental transaction manipulation; it is not a security boundary. Code that unwraps the connection must preserve manager-owned state.

The manager controls only transaction boundaries and connection state. It does not track, flush, or close statements, batches, result sets, or other JDBC resources.

The developer and generated JDBC code own those resources. They must observe these rules:

  • execute every pending batch entry before the transaction containing it is committed;
  • close statements, batches, result sets, and other resources before final completion;
  • keep only driver-supported resources open across an intermediate commit;
  • do not retain pending batch entries across an intermediate commit;
  • register a new synchronisation when a resource must participate in a later physical transaction.

The manager does not verify that batches are empty and does not execute them automatically. A registered resource is synchronised at the next completion attempt and its registration is then removed.