hibernate/hibernate-orm · error · TransactionException

Unable to rollback against JDBC Connection

Error message

Unable to rollback against JDBC Connection

What it means

Rollback-time failure: Connection.rollback() threw an SQLException, so Hibernate sets status FAILED_ROLLBACK and rethrows wrapped in this TransactionException. The physical connection and the server-side transaction are now in an unknown state — the rollback may or may not have reached the database. This exception is nearly always secondary: something already went wrong, and now cleanup is failing too.

Source

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

	}

	@Override
	public void rollback() {
		try {
			CONNECTION_LOGGER.preparingToRollbackViaConnectionRollback();
			status = TransactionStatus.ROLLING_BACK;
			if ( isPhysicallyConnected() ) {
				getConnectionForTransactionManagement().rollback();
			}
			else {
				errorIfClosed();
			}
			status = TransactionStatus.ROLLED_BACK;
			CONNECTION_LOGGER.transactionRolledBackViaConnectionRollback();
		}
		catch ( SQLException e ) {
			status = TransactionStatus.FAILED_ROLLBACK;
			throw new TransactionException( "Unable to rollback against JDBC Connection", e );
		}

		afterCompletion();
	}

	protected static boolean determineInitialAutoCommitMode(Connection providedConnection) {
		try {
			return providedConnection.getAutoCommit();
		}
		catch (SQLException e) {
			CONNECTION_LOGGER.unableToAscertainInitialAutoCommit();
			return true;
		}
	}

	@Override
	@Nonnull
	public TransactionStatus getStatus() {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Close the Session immediately afterwards and ensure the pool evicts the connection instead of returning it to circulation
  2. Check the database for leftover transactions/locks (pg_stat_activity, information_schema.INNODB_TRX) and kill them if needed
  3. Investigate the ORIGINAL error that triggered the rollback — this exception is noise around it
  4. Bound transaction size and driver socketTimeout so rollbacks can realistically finish

Example fix

// before
try { em.getTransaction().commit(); }
catch (Exception e) { em.getTransaction().rollback(); } // rollback itself throws

// after
try { em.getTransaction().commit(); }
catch (Exception e) {
  try { em.getTransaction().rollback(); }
  catch (Exception rb) { log.error("rollback failed; discarding session", rb); }
  finally { em.close(); } // make sure the broken connection is discarded, not pooled
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  tx.rollback();
} catch (org.hibernate.TransactionException rb) {
  // connection + server-side transaction state is unknown: close the session so the
  // pool discards the connection, then check the DB for orphaned transactions/locks
  log.error("rollback failed; discarding session", rb);
} finally {
  em.close();
}

Prevention

When it happens

Trigger: tx.rollback() (typically inside error handling after a failed flush or commit) when the connection is already broken: network gone, DB killed the session, the pool already reclaimed the connection, or rolling back a huge transaction exceeds the driver socket timeout.

Common situations: finally-block rollback during a DB outage; giant batch transactions whose rollback takes longer than socketTimeout; connection killed by the DB while the driver was sending ROLLBACK.

Related errors


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