{"record":{"id":"6d32aa66b9b19c1c","repo":"hibernate/hibernate-orm","slug":"entitymanagerfactory-is-already-closed","errorCode":null,"errorMessage":"EntityManagerFactory is already closed","messagePattern":"EntityManagerFactory is already closed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java","lineNumber":1062,"sourceCode":"\t * Closes the session factory, releasing all held resources.\n\t *\n\t * <ol>\n\t * <li>cleans up used cache regions and \"stops\" the cache provider.\n\t * <li>close the JDBC connection\n\t * <li>remove the JNDI binding\n\t * </ol>\n\t *\n\t * Note: Be aware that the sessionFactory instance still can\n\t * be a \"heavy\" object memory wise after close() has been called.  Thus\n\t * it is important to not keep referencing the instance to let the garbage\n\t * collector release the memory.\n\t */\n\t@Override\n\tpublic void close() {\n\t\tsynchronized (this) {\n\t\t\tif ( status != Status.OPEN ) {\n\t\t\t\tif ( getSessionFactoryOptions().getJpaCompliance().isJpaClosedComplianceEnabled() ) {\n\t\t\t\t\tthrow new IllegalStateException( \"EntityManagerFactory is already closed\" );\n\t\t\t\t}\n\t\t\t\tSESSION_FACTORY_LOGGER.alreadyClosed();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstatus = Status.CLOSING;\n\t\t}\n\n\t\tfinal var preCloseException = preClose();\n\n\t\ttry {\n\t\t\tSESSION_FACTORY_LOGGER.closingFactory( uuid );\n\t\t\tobserverChain.sessionFactoryClosing( this );\n\n\t\t\t// NOTE: the null checks below handle cases where close is called\n\t\t\t//\t\t from a failed attempt to create the SessionFactory\n\n\t\t\tif ( cacheAccess != null ) {\n\t\t\t\tcacheAccess.close();","sourceCodeStart":1044,"sourceCodeEnd":1080,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java#L1044-L1080","documentation":"SessionFactoryImpl.close() is lenient by default: closing an already-closed factory only logs. However, when JPA closed-object compliance is enabled (hibernate.jpa.compliance.closed=true, or AvailableSettings.JPA_COMPLIANCE_CLOSED), it follows the JPA spec and throws IllegalStateException('EntityManagerFactory is already closed') on a second close. The status check happens under a lock and only Status.OPEN proceeds to real closing.","triggerScenarios":"Calling close() twice on the same SessionFactory/EntityManagerFactory with JPA closed compliance enabled. Happens with multiple owners closing the same factory (application shutdown hook plus container @PreDestroy, or shared static factory), or defensive close() calls in several components.","commonSituations":"Spring context shutdown racing an application shutdown hook that also closes the EMF; test teardown (@AfterEach) closing an EMF that a shared test-context already closed; enabling jpa closed compliance globally for strictness and then tripping over previously tolerated double closes.","solutions":["Guard close calls: if (emf != null && emf.isOpen()) { emf.close(); }","Designate a single owner for the factory's lifecycle (the Spring container, one bootstrap class) and let only it close","In tests, close shared EMFs once in @AfterAll (or rely on the test context cache) rather than per-test","As a last resort disable hibernate.jpa.compliance.closed — but prefer fixing the double close since JPA containers may rely on the strict behavior"],"exampleFix":"// before\n@PreDestroy\npublic void shutdown() {\n    emf.close(); // second close when compliance enabled -> IllegalStateException\n}\n\n// after\n@PreDestroy\npublic void shutdown() {\n    if (emf != null && emf.isOpen()) {\n        emf.close();\n    }\n}","handlingStrategy":"validation","validationCode":"public static void closeQuietly(EntityManagerFactory emf) {\n    if (emf != null && emf.isOpen()) {\n        emf.close();\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    emf.close();\n} catch (IllegalStateException e) {\n    // already closed (JPA closed compliance enabled) — safe to ignore\n    LOG.debug(\"EntityManagerFactory already closed\", e);\n}","preventionTips":["Establish single ownership of the factory lifecycle; document which component closes it","Use isOpen() guards in every defensive close path (@PreDestroy, shutdown hooks)","Decide deliberately whether hibernate.jpa.compliance.closed is on; if it is, treat double close as a bug to fix, not to suppress"],"tags":["hibernate","jpa","lifecycle","double-close","compliance","shutdown"],"backgroundTag":"double-close","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}