hibernate/hibernate-orm · error · TransactionException
Transaction timeout expired
Error message
Transaction timeout expired
What it means
JdbcCoordinatorImpl.determineRemainingTransactionTimeOutPeriod() computes the milliseconds left before the configured transaction timeout instant; if the deadline has already passed it throws TransactionException('Transaction timeout expired'). The remaining-time value is used to apply statement query timeouts when preparing mutation and query statements (MutationStatementPreparerImpl and StatementPreparerImpl), so the exception typically surfaces during flush or statement preparation inside an over-deadline transaction.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java:323
@Override
public void setTransactionTimeOut(int seconds) {
transactionTimeOutInstant = System.currentTimeMillis() + ( seconds * 1000L );
}
@Override
public void flushBeforeTransactionCompletion() {
getJdbcSessionOwner().flushBeforeTransactionCompletion();
}
@Override
public int determineRemainingTransactionTimeOutPeriod() {
if ( transactionTimeOutInstant < 0 ) {
return -1;
}
final long millisecondsRemaining = transactionTimeOutInstant - System.currentTimeMillis();
if ( millisecondsRemaining <= 0L ) {
throw new TransactionException( "Transaction timeout expired" );
}
return Math.max( (int) (millisecondsRemaining / 1000), 1 );
}
@Override
public void afterStatementExecution() {
final var connectionReleaseMode = getLogicalConnection().resolvedConnectionReleaseMode();
if ( TRACE_ENABLED ) {
JDBC_LOGGER.statementExecutionComplete( connectionReleaseMode, hashCode() );
}
if ( connectionReleaseMode == AFTER_STATEMENT ) {
if ( !releasesEnabled ) {
JDBC_LOGGER.skippingAggressiveRelease( "manually disabled" );
}
else if ( hasRegisteredResources() ) {
JDBC_LOGGER.skippingAggressiveRelease( "registered resources" );
}
else {View on GitHub (pinned to fad1729dce)
Solutions
- Raise the transaction timeout for the long-running unit of work (e.g. @Transactional(timeout = 300))
- Optimize or split the work: chunk batches into multiple transactions, fix slow statements and lock contention
- Keep entity counts per transaction small so flush does not blow the deadline
Example fix
// before
@Transactional(timeout = 5)
public void importAll(List<Row> rows) { rows.forEach(repo::save); }
// after
@Transactional(timeout = 300)
public void importAll(List<Row> rows) { /* also consider chunking */ } Defensive patterns
Strategy: retry
Validate before calling
// check remaining time in long loops and commit chunks early
long deadline = System.currentTimeMillis() + timeoutMillis;
for (Row r : rows) {
if (System.currentTimeMillis() > deadline - margin) { commitChunkAndStartNewTx(); }
save(r);
} Try / catch
catch (TransactionException e) {
if (e.getMessage().contains("Transaction timeout expired")) {
// rollback and retry the unit of work with a fresh transaction
}
} Prevention
- Size transaction timeouts to the real work (rows * per-row cost)
- Chunk batch jobs into multiple transactions instead of one giant one
- Monitor slow statements and lock waits that eat the budget
When it happens
Trigger: A transaction timeout was set (Spring @Transactional(timeout=...), JTA transaction timeout, or transaction manager timeout propagated to setTransactionTimeOutInstant) and elapsed before Hibernate finished preparing/executing statements — long flushes, slow SQL, lock waits, or oversized batch jobs.
Common situations: Batch imports inside one @Transactional; pessimistic-lock waits; a statement-level query timeout being derived from the remaining transaction time; heavy first-level-cache flushes at commit.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Transaction was not properly begun/started
- Transaction was marked for rollback only
- cannot recreate collection while filter is enabled: " + coll
- Newer version [" + latestVersion + "] of entity [" + infoStr
- Instance of '" + entityName + "' references an unsaved trans
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/5de0575a2bf4de4c.
Report an issue: GitHub.