hibernate/hibernate-orm · critical · TransactionException

Unable to commit against JDBC Connection

Error message

Unable to commit against JDBC Connection

What it means

Commit-time failure: the logical connection's Connection.commit() threw an SQLException. Hibernate sets status FAILED_COMMIT, attempts a last-ditch rollback() (whose own failure is attached as a suppressed exception), then wraps the commit error in this TransactionException. Because the connection died mid-commit, whether the data actually committed is indeterminate and must be verified before retrying.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/AbstractLogicalConnectionImplementor.java:110

			status = TransactionStatus.COMMITTED;
			CONNECTION_LOGGER.transactionCommittedViaConnectionCommit();
		}
		catch (SQLException e) {
			// commit failed, the current status of the
			// transaction is ambiguous
			status = TransactionStatus.FAILED_COMMIT;
			// make a last ditch attempt to roll it back
			try {
				getConnectionForTransactionManagement().rollback();
				status = TransactionStatus.ROLLED_BACK;
			}
			catch (SQLException e2) {
				e.addSuppressed( e2 );
				JDBC_LOGGER.encounteredFailureRollingBackFailedCommit( e2 );
				// at this point we can't really know for
				// sure what happened to the transaction
			}
			throw new TransactionException( "Unable to commit against JDBC Connection", e );
		}
	}

	protected void afterCompletion() {
		// by default, nothing to do
	}

	protected void resetConnection(boolean initiallyAutoCommit) {
		try {
			if ( initiallyAutoCommit ) {
				CONNECTION_LOGGER.reenablingAutoCommitAfterJdbcTransaction();
				getConnectionForTransactionManagement().setAutoCommit( true );
				status = TransactionStatus.NOT_ACTIVE;
			}
		}
		catch ( Exception e ) {
			CONNECTION_LOGGER.couldNotReEnableAutoCommit( e );
		}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Read the cause SQLException's SQLState: 08xxx (connection) and 40001/40P01/55P03 (deadlock/lock timeout) are transient; constraint classes (23xxx) are permanent data problems
  2. Before retrying, verify whether the work actually committed (re-query the data, check in-doubt/XA recovery logs) to avoid duplicate effects
  3. For transient classes, retry the entire transaction from the beginning with a new Session and idempotent logic
  4. Increase driver/pool timeouts (socketTimeout, connectionTimeout) if legitimate commits are long

Example fix

// before
em.getTransaction().commit(); // TransactionException: Unable to commit against JDBC Connection

// after
try {
  em.getTransaction().commit();
} catch (org.hibernate.TransactionException e) {
  SQLException sql = findSqlException(e);
  String state = sql == null ? null : sql.getSQLState();
  boolean transientFailure = state != null
      && (state.startsWith("08") || "40001".equals(state) || "40P01".equals(state) || "55P03".equals(state));
  if (transientFailure) { /* verify outcome, then retry unit of work with new EntityManager */ }
  else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

// before committing, confirm the connection is still usable
em.unwrap(org.hibernate.Session.class).doReturningWork(c -> c.isValid(5));

Type guard

static SQLException findSqlException(Throwable t) {
  for (Throwable c = t; c != null; c = c.getCause()) {
    if (c instanceof SQLException s) return s;
  }
  return null;
}

Try / catch

try {
  tx.commit();
} catch (org.hibernate.TransactionException e) {
  SQLException sql = findSqlException(e);
  String s = sql == null ? null : sql.getSQLState();
  boolean transientFailure = s != null
      && (s.startsWith("08") || "40001".equals(s) || "40P01".equals(s) || "55P03".equals(s));
  // transient: FIRST verify whether the work actually committed (re-query),
  // then retry the idempotent unit of work on a new EntityManager; otherwise rethrow
}

Prevention

When it happens

Trigger: tx.commit() on a resource-local session where Connection.commit() throws: deferred/deferrable constraint violations surfacing only at commit, deadlock or lock-wait timeout detected at commit time, connection reset between flush and commit, or a socket/stream timeout on a long-running commit.

Common situations: Network blip exactly at commit; long transactions exceeding driver socketTimeout; PostgreSQL deferrable constraints failing at COMMIT; connection killed by an LB idle policy or DBA mid-commit.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/90c4d019d795c626. Report an issue: GitHub.