{"record":{"id":"f6f6bc6c7638174a","repo":"hibernate/hibernate-orm","slug":"connection-s-passed-back-to-s-was-not-the-one","errorCode":null,"errorMessage":"Connection [%s] passed back to %s was not the one obtained [%s] from it","messagePattern":"Connection \\[(.+?)\\] passed back to (.+?) was not the one obtained \\[(.+?)\\] from it","errorType":"exception","errorClass":"PersistenceException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/tool/schema/internal/exec/JdbcConnectionAccessConnectionProviderImpl.java","lineNumber":72,"sourceCode":"\t\t\t}\n\t\t}\n\t\tcatch (SQLException ignore) {\n\t\t\twasInitiallyAutoCommit = false;\n\t\t}\n\n\t\tJDBC_LOGGER.initialAutoCommit( wasInitiallyAutoCommit );\n\t\tthis.wasInitiallyAutoCommit = wasInitiallyAutoCommit;\n\t}\n\n\t@Override\n\tpublic Connection obtainConnection() throws SQLException {\n\t\treturn jdbcConnection;\n\t}\n\n\t@Override\n\tpublic void releaseConnection(Connection connection) throws SQLException {\n\t\tif ( connection != this.jdbcConnection ) {\n\t\t\tthrow new PersistenceException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\"Connection [%s] passed back to %s was not the one obtained [%s] from it\",\n\t\t\t\t\t\t\tconnection,\n\t\t\t\t\t\t\tJdbcConnectionAccessConnectionProviderImpl.class.getName(),\n\t\t\t\t\t\t\tjdbcConnection\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\n\t\t// Reset auto-commit\n\t\tif ( !wasInitiallyAutoCommit ) {\n\t\t\ttry {\n\t\t\t\tif ( jdbcConnection.getAutoCommit() ) {\n\t\t\t\t\tjdbcConnection.setAutoCommit( false );\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (SQLException exception) {\n\t\t\t\tJDBC_LOGGER.unableToResetAutoCommitDisabled( exception );","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/exec/JdbcConnectionAccessConnectionProviderImpl.java#L54-L90","documentation":"Thrown by Hibernate's schema management tooling (SchemaExport/SchemaUpdate/SchemaMigrator) when the JDBC Connection handed back to JdbcConnectionAccessConnectionProviderImpl.releaseConnection() is not the exact same instance obtainConnection() returned. The wrapper hands out one single Connection for the whole schema operation and enforces identity on release, because it must restore the connection's original auto-commit state before giving it back to the ConnectionProvider. A different instance therefore means the contract obtain/release-same-connection was broken somewhere in between.","triggerScenarios":"Running schema generation/migration with a ConnectionProvider-backed JdbcConnectionAccess, then calling releaseConnection() with a connection other than the one obtained: a wrapped/proxied connection (logging, metrics, tenant-routing delegates), a different pooled connection, or custom GenerationTarget/JdbcConnectionAccess code that swaps the instance. Also triggered by pools that return a fresh dynamic proxy per close() path so == identity fails.","commonSituations":"Custom GenerationTarget or custom schema-management integrations (e.g. reactive/tooling wrappers) that wrap connections; connection pools issuing non-identical proxies; user code mixing connections from two providers during hbm2ddl.","solutions":["Pass the exact Connection instance returned by obtainConnection() to releaseConnection() — never a wrapper or delegate.","Remove or unwrap connection proxies around schema generation; unwrap to the underlying vendor connection before releasing.","If you implement JdbcConnectionAccess or GenerationTarget yourself, obtain and release through the same provider path so instance identity holds.","Align/upgrade your integration (e.g. Hibernate Reactive, custom tools) with the matching hibernate-core version — identity handling has been patched across releases."],"exampleFix":"// before: releasing a wrapped connection\nfinal Connection conn = access.obtainConnection();\naccess.releaseConnection( new LoggingConnectionWrapper( conn ) ); // PersistenceException\n\n// after: release the exact instance that was obtained\nfinal Connection conn = access.obtainConnection();\ntry {\n    // run DDL against conn\n}\nfinally {\n    access.releaseConnection( conn );\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    schemaExport.execute( EnumSet.of( TargetType.DATABASE ), metadata );\n}\ncatch ( PersistenceException e ) {\n    if ( e.getMessage() != null && e.getMessage().contains( \"was not the one obtained\" ) ) {\n        // a GenerationTarget or custom access released a different Connection instance\n        throw new IllegalStateException( \"Schema tooling connection mismatch\", e );\n    }\n    throw e;\n}","preventionTips":["Hold the Connection returned by obtainConnection() in a local variable and release exactly that reference in finally.","Never wrap or proxy connections between obtain and release during schema generation.","Use one ConnectionProvider for the whole schema operation; do not mix sources.","Avoid custom GenerationTargets unless you replicate Hibernate's obtain/release pairing exactly."],"tags":["hibernate","jdbc","schema-management","connection-handling","persistence-exception"],"backgroundTag":"jdbc-connection-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}