Class TimeoutAwareQueryRunner

java.lang.Object
org.apache.commons.dbutils.AbstractQueryRunner
org.apache.commons.dbutils.QueryRunner
com.ultikits.ultitools.interfaces.impl.data.TimeoutAwareQueryRunner

class TimeoutAwareQueryRunner extends org.apache.commons.dbutils.QueryRunner
A QueryRunner that applies a per-statement JDBC query timeout, computed against a shared transaction deadline rather than a fixed value (D-10 -- Spring's approach: each statement gets the time remaining in the transaction's budget, not a fresh full allowance). This is package-private framework plumbing for AbstractRelationalDataOperator.

Overrides AbstractQueryRunner.prepareStatement(Connection, String) -- the one override point AbstractQueryRunner's own javadoc documents for exactly this purpose ("Subclasses can override this method to provide special PreparedStatement configuration if needed"), confirmed by reading commons-dbutils-1.8.1-sources.jar. Every other QueryRunner behavior -- parameter binding, result-set handling, resource cleanup -- is untouched.

deadlineSupplier is read fresh on every statement, not captured once: the operator holding this runner outlives any single transaction, so the deadline it should honor changes across calls. A null result means "no timeout configured" -- no active transaction, or @Transactional(timeout = 0) (the default) -- and Statement.setQueryTimeout(int) is not called at all, leaving the statement at the driver's own default.

The floor. An already-exhausted budget produces MIN_QUERY_TIMEOUT_SECONDS (currently 1), never 0 and never a negative number. In the JDBC contract, setQueryTimeout(0) means "no limit" -- silently handing an exhausted budget an unlimited timeout would be the opposite of what a timeout is for. A negative value is rejected by the driver outright. Flooring at the smallest real limit is the only choice that fails fast instead of either of those.

Deliberately does not attempt to cancel or interrupt a statement already in flight when its budget expires -- that would be a wall-clock bound on the surrounding method body, which REQUIREMENTS.md rules out as not implementable on plain JDBC (WIRE-14). The bound here is per-statement, by construction, applied before the statement is ever executed.

Since:
6.3.0
Author:
wisdomme
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    private final Supplier<Long>
    Supplies the active transaction's deadline as a System.nanoTime() value, or null when no timeout is configured for the current statement (no active transaction, or a zero/negative @Transactional(timeout=)).
    (package private) static final int
    The smallest query timeout, in seconds, this runner will ever apply.

    Fields inherited from class org.apache.commons.dbutils.AbstractQueryRunner

    ds
  • Constructor Summary

    Constructors
    Constructor
    Description
    TimeoutAwareQueryRunner(DataSource dataSource, Supplier<Long> deadlineSupplier)
    Creates a new timeout-aware query runner.
  • Method Summary

    Modifier and Type
    Method
    Description
     
    (package private) static int
    remainingSecondsFloored(long deadlineNanos)
    Converts a System.nanoTime() deadline into the whole seconds remaining, floored at MIN_QUERY_TIMEOUT_SECONDS so an exhausted or already-passed deadline still produces a real, enforced limit rather than 0 ("no limit") or a negative value (which the JDBC driver rejects).

    Methods inherited from class org.apache.commons.dbutils.QueryRunner

    batch, batch, execute, execute, execute, execute, insert, insert, insert, insert, insertBatch, insertBatch, query, query, query, query, query, query, query, query, update, update, update, update, update, update

    Methods inherited from class org.apache.commons.dbutils.AbstractQueryRunner

    close, close, close, closeQuietly, closeQuietly, closeQuietly, fillStatement, fillStatement, fillStatementWithBean, fillStatementWithBean, getDataSource, getParameterMetaData, isPmdKnownBroken, prepareCall, prepareConnection, prepareStatement, rethrow, wrap

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • MIN_QUERY_TIMEOUT_SECONDS

      static final int MIN_QUERY_TIMEOUT_SECONDS
      The smallest query timeout, in seconds, this runner will ever apply. setQueryTimeout(0) means "no limit" in the JDBC contract, so an exhausted deadline must never round down to it -- 1 is the smallest value that still means "a real, enforced limit."
      See Also:
    • deadlineSupplier

      private final Supplier<Long> deadlineSupplier
      Supplies the active transaction's deadline as a System.nanoTime() value, or null when no timeout is configured for the current statement (no active transaction, or a zero/negative @Transactional(timeout=)). Read fresh per statement -- see the class javadoc.
  • Constructor Details

    • TimeoutAwareQueryRunner

      TimeoutAwareQueryRunner(DataSource dataSource, Supplier<Long> deadlineSupplier)
      Creates a new timeout-aware query runner.
      Parameters:
      dataSource - the data source to use for connections
      deadlineSupplier - supplies the current transaction's deadline (System.nanoTime() value), or null when no timeout applies
  • Method Details

    • prepareStatement

      protected PreparedStatement prepareStatement(Connection conn, String sql) throws SQLException
      Overrides:
      prepareStatement in class org.apache.commons.dbutils.AbstractQueryRunner
      Throws:
      SQLException
    • remainingSecondsFloored

      static int remainingSecondsFloored(long deadlineNanos)
      Converts a System.nanoTime() deadline into the whole seconds remaining, floored at MIN_QUERY_TIMEOUT_SECONDS so an exhausted or already-passed deadline still produces a real, enforced limit rather than 0 ("no limit") or a negative value (which the JDBC driver rejects).

      Package-visible (not private) so AbstractRelationalDataOperator's two direct PreparedStatement call sites -- insertAll/updateAll, which bypass QueryRunner entirely -- apply the identical floor this class does, rather than a second, potentially-diverging copy of the same arithmetic.

      Parameters:
      deadlineNanos - the deadline, as a System.nanoTime() value
      Returns:
      the query timeout to apply, in seconds, always >= MIN_QUERY_TIMEOUT_SECONDS