{"record":{"id":"74d14443cc3be5cd","repo":"hibernate/hibernate-orm","slug":"cannot-recreate-collection-while-filter-is-enabled","errorCode":null,"errorMessage":"cannot recreate collection while filter is enabled: \" + collectionInfoString( persister, collection, key, session )","messagePattern":"cannot recreate collection while filter is enabled: \" \\+ collectionInfoString\\( persister, collection, key, session \\)","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/action/internal/CollectionUpdateAction.java","lineNumber":112,"sourceCode":"\t\t\t\t// The collection should still be dirty.\n\t\t\t\tthrow new AssertionFailure( \"collection is not dirty\" );\n\t\t\t}\n\t\t\t// Do nothing - we only need to notify the cache\n\t\t}\n\t\telse {\n\t\t\tfinal var eventMonitor = session.getEventMonitor();\n\t\t\tfinal var event = eventMonitor.beginCollectionUpdateEvent();\n\t\t\tboolean success = false;\n\t\t\ttry {\n\t\t\t\tif ( !affectedByFilters && collection.empty() ) {\n\t\t\t\t\tif ( !emptySnapshot ) {\n\t\t\t\t\t\tpersister.remove( key, session );\n\t\t\t\t\t}\n\t\t\t\t\t//TODO: else we really shouldn't have sent an update event to JFR\n\t\t\t\t}\n\t\t\t\telse if ( collection.needsRecreate( persister ) ) {\n\t\t\t\t\tif ( affectedByFilters ) {\n\t\t\t\t\t\tthrow new HibernateException( \"cannot recreate collection while filter is enabled: \"\n\t\t\t\t\t\t\t\t\t\t\t\t+ collectionInfoString( persister, collection, key, session ) );\n\t\t\t\t\t}\n\t\t\t\t\tif ( !emptySnapshot ) {\n\t\t\t\t\t\tpersister.remove( key, session );\n\t\t\t\t\t}\n\t\t\t\t\tpersister.recreate( collection, key, session );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tpersister.deleteRows( collection, key, session );\n\t\t\t\t\tpersister.updateRows( collection, key, session );\n\t\t\t\t\tpersister.insertRows( collection, key, session );\n\t\t\t\t}\n\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\teventMonitor.completeCollectionUpdateEvent( event, key, persister.getRole(), success, session );\n\t\t\t}\n\t\t}","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionUpdateAction.java#L94-L130","documentation":"During flush, CollectionUpdateAction (CollectionUpdateAction.java:112) handles updates of owned collections. When a collection 'needs recreate' (true for bag-style collections such as unindexed Lists, arrays, and maps without a collection id, because Hibernate cannot diff rows and must delete-all + reinsert), and a @Filter is currently enabled that affects the collection, Hibernate throws HibernateException(\"cannot recreate collection while filter is enabled: ...\"). The filter changes which rows are visible, so a delete-all/recreate would either drop filtered-out rows or insert rows the filter should hide - Hibernate refuses rather than corrupt data.","triggerScenarios":"session.enableFilter(...) active on an entity whose collection is a bag (List without @OrderColumn), primitive array, or map without collection id; then clearing the collection or mutating it in a way that forces needsRecreate()==true; flush/commit triggers CollectionUpdateAction and the guard fires.","commonSituations":"Soft-delete or tenant filters (@Filter on entities) combined with @OneToMany List fields; multitenancy filters active for the whole request while business code clears/replaces a collection; upgrading mappings from Set to List and suddenly hitting recreate semantics.","solutions":["Change the collection mapping so row diffs are possible: add @OrderColumn (indexed list), use a Set, or map with a collection id (@CollectionId / idbag) - then needsRecreate() is false and the guard is skipped","Temporarily disable the filter before mutating the collection: session.disableFilter(\"name\"), flush, then re-enable","Avoid clearing/replacing filtered bag collections while filters are on; mutate incrementally (add/remove elements) instead of clear()+addAll()","If the filter logically should not apply to that collection, move the @Filter definition so it does not affect the collection role"],"exampleFix":"// before - bag + enabled filter forces recreate and the exception\n@OneToMany(mappedBy = \"order\", cascade = ALL)\nprivate List<Item> items = new ArrayList<>();  // unindexed bag\n\n// after - indexed collection can be diffed row-wise, no recreate needed\n@OneToMany(mappedBy = \"order\", cascade = ALL)\n@OrderColumn(name = \"position\")\nprivate List<Item> items = new ArrayList<>();","handlingStrategy":"validation","validationCode":"// before clearing/replacing a collection, make sure no filter affects it\npublic void replaceItems(Session session, Order order, List<Item> newItems) {\n    Filter f = session.getEnabledFilter(\"softDelete\");\n    if (f != null) {\n        throw new IllegalStateException(\n            \"disable filter 'softDelete' before replacing a bag collection, or map it as indexed/idbag\");\n    }\n    order.getItems().clear();\n    order.getItems().addAll(newItems);\n}","typeGuard":null,"tryCatchPattern":"try {\n    session.flush();\n} catch (HibernateException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"cannot recreate collection\")) {\n        // disable filters, re-map collection, or reject the operation with a domain error\n    } else throw e;\n}","preventionTips":["Map filtered entities' collections as indexed (@OrderColumn), Set, or idbag so recreate is never needed","Wrap filter enable/disable symmetrically around the units of work that need them","Never clear()+refill bag collections while tenant/soft-delete filters are active"],"tags":["collections","filters","flush","mapping","hibernate"],"backgroundTag":"filtered-collection-mutation","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}