Class DataSourceTransactionManager

java.lang.Object
com.ultikits.ultitools.manager.DataSourceTransactionManager
All Implemented Interfaces:
JdbcTransactionManager, TransactionManager

@Internal public class DataSourceTransactionManager extends Object implements JdbcTransactionManager
DataSource-based implementation of TransactionManager, and its JDBC-specific sub-interface JdbcTransactionManager.

Uses ThreadLocal to associate transactions with threads, ensuring thread safety. Each thread can have at most one active transaction at a time.

Since:
6.2.0
Author:
wisdomme
  • Field Details

  • Constructor Details

    • DataSourceTransactionManager

      public DataSourceTransactionManager(DataSource dataSource)
      Creates a new DataSourceTransactionManager.
      Parameters:
      dataSource - the data source to use for connections
  • Method Details

    • getConnection

      public Connection getConnection()
      Description copied from interface: JdbcTransactionManager
      Gets the current connection, creating a new one if necessary.

      If a transaction is active, returns the connection associated with it. Otherwise, returns a new connection from the data source.

      Specified by:
      getConnection in interface JdbcTransactionManager
      Specified by:
      getConnection in interface TransactionManager
      Returns:
      the current connection
    • begin

      public void begin()
      Description copied from interface: TransactionManager
      Begins a new transaction.

      Creates a new connection, disables auto-commit, and associates it with the current thread.

      Specified by:
      begin in interface TransactionManager
    • commit

      public void commit()
      Description copied from interface: TransactionManager
      Commits the current transaction.

      Commits all changes made during the transaction and releases the connection.

      Specified by:
      commit in interface TransactionManager
    • rollback

      public void rollback()
      Description copied from interface: TransactionManager
      Rolls back the current transaction.

      Discards all changes made during the transaction and releases the connection.

      Specified by:
      rollback in interface TransactionManager
    • hasActiveTransaction

      public boolean hasActiveTransaction()
      Description copied from interface: TransactionManager
      Checks if there is an active transaction on the current thread.
      Specified by:
      hasActiveTransaction in interface TransactionManager
      Returns:
      true if a transaction is active
    • setIsolationLevel

      public void setIsolationLevel(int level)
      Description copied from interface: JdbcTransactionManager
      Sets the isolation level for the current transaction.

      Must be called after begin() and before any statements are executed.

      Specified by:
      setIsolationLevel in interface JdbcTransactionManager
      Specified by:
      setIsolationLevel in interface TransactionManager
      Parameters:
      level - the JDBC isolation level constant
    • setReadOnly

      public void setReadOnly(boolean readOnly)
      Description copied from interface: JdbcTransactionManager
      Sets read-only mode for the current transaction.
      Specified by:
      setReadOnly in interface JdbcTransactionManager
      Specified by:
      setReadOnly in interface TransactionManager
      Parameters:
      readOnly - true for read-only mode
    • setTimeout

      public void setTimeout(int seconds)
      Sets the timeout for the current transaction.

      Note: Not all databases/drivers support transaction timeout.

      D-10: records a deadline on the active DataSourceTransactionManager.TransactionContext rather than a wall-clock bound on the transaction as a whole -- getTimeoutDeadlineNanos() exposes it so each statement issued afterward can be given the time remaining, not a fresh full allowance. seconds <= 0 is inert (no deadline recorded), matching @Transactional(timeout=0)'s default -- TransactionInterceptor itself never calls this method for a non-positive value in the first place, so this is a second, defensive guard for any other caller. Called with no active transaction, this logs a warning and records nothing, the same shape commit()/rollback() already use for that case.

      Specified by:
      setTimeout in interface TransactionManager
      Parameters:
      seconds - the timeout in seconds
    • getTimeoutDeadlineNanos

      public Long getTimeoutDeadlineNanos()
      Returns the System.nanoTime() value at which the active transaction's timeout budget expires, or null when no timeout is configured for the active transaction -- no active transaction at all, or @Transactional(timeout=0) (the default).

      D-10: consulted per statement (by TimeoutAwareQueryRunner and the two direct PreparedStatement call sites AbstractRelationalDataOperator.insertAll/ updateAll build themselves) to compute the time remaining in the budget, not a value captured once -- the caller re-reads this on every statement.

      The default implementation always returns null, meaning "never enforced" -- the same posture a backend whose setTimeout(int) refuses any positive value outright (rather than ever recording a deadline) naturally has, since it never has anything to report here either. A backend that wants per-statement enforcement overrides this together with TransactionManager.setTimeout(int) -- overriding only one half of the pair leaves the other silently inert, the same caution TransactionManager.suspend()'s javadoc gives for its pair.

      Reads DataSourceTransactionManager.TransactionContext.timeoutDeadlineNanos off the active context -- null when no transaction is active, or when setTimeout(int) was never called (or called only with a non-positive value) for it. suspend()/resume(Object) detach and restore the whole DataSourceTransactionManager.TransactionContext object, so a suspended-then-resumed transaction's deadline travels with it automatically -- no separate handling needed here.

      Specified by:
      getTimeoutDeadlineNanos in interface TransactionManager
      Returns:
      the deadline, as a System.nanoTime() value, or null if none is active
    • getTransactionDepth

      public int getTransactionDepth()
      Description copied from interface: TransactionManager
      Gets the current transaction depth (for nested transactions).
      Specified by:
      getTransactionDepth in interface TransactionManager
      Returns:
      the transaction depth (0 if no transaction)
    • suspend

      public Object suspend()
      Suspends the current transaction, if any, detaching it from this thread so an independent transaction can begin in its place. Backs Propagation.REQUIRES_NEW and Propagation.NOT_SUPPORTED (D-09).

      Deliberately typed to return an opaque Object rather than a backend-specific context type: a JDBC-backed manager suspends a Connection-holding context, a JSON snapshot manager (02-05) suspends a set of operator snapshots (D-03) - neither shape belongs on this base interface. Pass whatever this returns to TransactionManager.resume(Object) to restore it; the caller must not otherwise inspect or reuse the returned value.

      The default implementation does not support suspension: it always returns null, meaning nothing was suspended. A backend that maintains thread-bound transaction state (JDBC, JSON) must override this together with TransactionManager.resume(Object) - overriding only one of the pair breaks the contract silently.

      Detaches the current DataSourceTransactionManager.TransactionContext (connection included) from this thread and pushes it onto suspendedStack, leaving hasActiveTransaction() false. A begin() that runs while the original stays suspended opens a fully independent transaction, on its own connection - the suspended one is never touched until resume(Object) restores it.

      Specified by:
      suspend in interface TransactionManager
      Returns:
      an opaque handle to resume later, or null if no transaction was active
    • resume

      public void resume(Object suspended)
      Restores a transaction previously detached by TransactionManager.suspend().

      null is a no-op - it means TransactionManager.suspend() found nothing active in the first place, so there is nothing to restore. Passing a handle this TransactionManager did not itself produce is a programming error.

      The default implementation is a no-op, pairing with TransactionManager.suspend()'s default of always returning null.

      null is a no-op - suspend() found nothing active in the first place, so there is nothing on suspendedStack for this call to pop. A non-null handle must be exactly what this manager's own suspend() most recently returned; restoring it sets it back as the active context (same DataSourceTransactionManager.TransactionContext instance, same Connection, same depth) and pops it off suspendedStack, removing the ThreadLocal entirely once the stack is empty rather than leaving a dangling empty Deque behind.

      Specified by:
      resume in interface TransactionManager
      Parameters:
      suspended - the handle returned by a prior TransactionManager.suspend() call, or null
      Throws:
      IllegalArgumentException - if suspended is non-null but was not produced by this manager's own suspend()
    • cleanup

      Cleans up the transaction context.
    • getCurrentContext

      Gets the current transaction context (for testing).

      Instance method, not static, per the same FOUND-04 fix as contextHolder itself: two DataSourceTransactionManager instances now have independent state.