{"record":{"id":"90c4d019d795c626","repo":"hibernate/hibernate-orm","slug":"unable-to-commit-against-jdbc-connection","errorCode":null,"errorMessage":"Unable to commit against JDBC Connection","messagePattern":"Unable to commit against JDBC Connection","errorType":"exception","errorClass":"TransactionException","httpStatus":null,"severity":"critical","filePath":"hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/AbstractLogicalConnectionImplementor.java","lineNumber":110,"sourceCode":"\t\t\tstatus = TransactionStatus.COMMITTED;\n\t\t\tCONNECTION_LOGGER.transactionCommittedViaConnectionCommit();\n\t\t}\n\t\tcatch (SQLException e) {\n\t\t\t// commit failed, the current status of the\n\t\t\t// transaction is ambiguous\n\t\t\tstatus = TransactionStatus.FAILED_COMMIT;\n\t\t\t// make a last ditch attempt to roll it back\n\t\t\ttry {\n\t\t\t\tgetConnectionForTransactionManagement().rollback();\n\t\t\t\tstatus = TransactionStatus.ROLLED_BACK;\n\t\t\t}\n\t\t\tcatch (SQLException e2) {\n\t\t\t\te.addSuppressed( e2 );\n\t\t\t\tJDBC_LOGGER.encounteredFailureRollingBackFailedCommit( e2 );\n\t\t\t\t// at this point we can't really know for\n\t\t\t\t// sure what happened to the transaction\n\t\t\t}\n\t\t\tthrow new TransactionException( \"Unable to commit against JDBC Connection\", e );\n\t\t}\n\t}\n\n\tprotected void afterCompletion() {\n\t\t// by default, nothing to do\n\t}\n\n\tprotected void resetConnection(boolean initiallyAutoCommit) {\n\t\ttry {\n\t\t\tif ( initiallyAutoCommit ) {\n\t\t\t\tCONNECTION_LOGGER.reenablingAutoCommitAfterJdbcTransaction();\n\t\t\t\tgetConnectionForTransactionManagement().setAutoCommit( true );\n\t\t\t\tstatus = TransactionStatus.NOT_ACTIVE;\n\t\t\t}\n\t\t}\n\t\tcatch ( Exception e ) {\n\t\t\tCONNECTION_LOGGER.couldNotReEnableAutoCommit( e );\n\t\t}","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/AbstractLogicalConnectionImplementor.java#L92-L128","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the cause SQLException's SQLState: 08xxx (connection) and 40001/40P01/55P03 (deadlock/lock timeout) are transient; constraint classes (23xxx) are permanent data problems","Before retrying, verify whether the work actually committed (re-query the data, check in-doubt/XA recovery logs) to avoid duplicate effects","For transient classes, retry the entire transaction from the beginning with a new Session and idempotent logic","Increase driver/pool timeouts (socketTimeout, connectionTimeout) if legitimate commits are long"],"exampleFix":"// before\nem.getTransaction().commit(); // TransactionException: Unable to commit against JDBC Connection\n\n// after\ntry {\n  em.getTransaction().commit();\n} catch (org.hibernate.TransactionException e) {\n  SQLException sql = findSqlException(e);\n  String state = sql == null ? null : sql.getSQLState();\n  boolean transientFailure = state != null\n      && (state.startsWith(\"08\") || \"40001\".equals(state) || \"40P01\".equals(state) || \"55P03\".equals(state));\n  if (transientFailure) { /* verify outcome, then retry unit of work with new EntityManager */ }\n  else throw e;\n}","handlingStrategy":"retry","validationCode":"// before committing, confirm the connection is still usable\nem.unwrap(org.hibernate.Session.class).doReturningWork(c -> c.isValid(5));","typeGuard":"static SQLException findSqlException(Throwable t) {\n  for (Throwable c = t; c != null; c = c.getCause()) {\n    if (c instanceof SQLException s) return s;\n  }\n  return null;\n}","tryCatchPattern":"try {\n  tx.commit();\n} catch (org.hibernate.TransactionException e) {\n  SQLException sql = findSqlException(e);\n  String s = sql == null ? null : sql.getSQLState();\n  boolean transientFailure = s != null\n      && (s.startsWith(\"08\") || \"40001\".equals(s) || \"40P01\".equals(s) || \"55P03\".equals(s));\n  // transient: FIRST verify whether the work actually committed (re-query),\n  // then retry the idempotent unit of work on a new EntityManager; otherwise rethrow\n}","preventionTips":["Keep transactions short so commit latency stays well under any timeout","Make units of work idempotent so a retry after an in-doubt commit is safe","Set driver/pool timeouts (socketTimeout, connectionTimeout) above worst-case commit duration","Validate deferrable-constraint data before commit rather than relying on commit-time checks"],"tags":["hibernate","jdbc","transaction","commit","in-doubt-transaction"],"backgroundTag":"jdbc-commit-failed","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}