{"record":{"id":"8bcd8781655fd451","repo":"hibernate/hibernate-orm","slug":"the-query-attempts-to-update-an-immutable-entity","errorCode":null,"errorMessage":"The query attempts to update an immutable entity: ${querySpaces} (set 'hibernate.query.immutable_entity_update_query_handling_mode' to suppress)","messagePattern":"The query attempts to update an immutable entity: (.+?) \\(set 'hibernate\\.query\\.immutable_entity_update_query_handling_mode' to suppress\\)","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/update/SqmUpdateStatement.java","lineNumber":177,"sourceCode":"\t\t\tSqmUtil.validateCriteriaTree( this );\n\t\t}\n\t\tverifyUpdateTypesMatch();\n\t}\n\n\tprivate void verifyImmutableEntityUpdate(@Nullable String hql) {\n\t\tfinal EntityPersister persister =\n\t\t\t\tnodeBuilder().getMappingMetamodel().getEntityDescriptor( getTarget().getEntityName() );\n\t\tif ( !persister.isMutable() ) {\n\t\t\tfinal String querySpaces = Arrays.toString( persister.getQuerySpaces() );\n\t\t\tswitch ( nodeBuilder().getImmutableEntityUpdateQueryHandlingMode() ) {\n\t\t\t\tcase ALLOW :\n\t\t\t\t\tCORE_LOGGER.immutableEntityUpdateQueryAllowed( hql, querySpaces );\n\t\t\t\t\tbreak;\n\t\t\t\tcase WARNING:\n\t\t\t\t\tCORE_LOGGER.immutableEntityUpdateQuery( hql, querySpaces );\n\t\t\t\t\tbreak;\n\t\t\t\tcase EXCEPTION:\n\t\t\t\t\tthrow new HibernateException( \"The query attempts to update an immutable entity: \"\n\t\t\t\t\t\t\t\t\t\t\t\t+ querySpaces\n\t\t\t\t\t\t\t\t\t\t\t\t+ \" (set '\"\n\t\t\t\t\t\t\t\t\t\t\t\t+ AvailableSettings.IMMUTABLE_ENTITY_UPDATE_QUERY_HANDLING_MODE\n\t\t\t\t\t\t\t\t\t\t\t\t+ \"' to suppress)\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void verifyUpdateTypesMatch() {\n\t\tfinal List<SqmAssignment<?>> assignments = getSetClause().getAssignments();\n\t\tfor ( int i = 0; i < assignments.size(); i++ ) {\n\t\t\tfinal SqmAssignment<?> assignment = assignments.get( i );\n\t\t\tfinal SqmPath<?> targetPath = assignment.getTargetPath();\n\t\t\tfinal SqmExpression<?> expression = assignment.getValue();\n\t\t\tassertAssignable( null, targetPath, expression, nodeBuilder() );\n\t\t}\n\t}\n","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/update/SqmUpdateStatement.java#L159-L195","documentation":"Entities mapped @Immutable (or mutable=false) must not be updated by design, and Hibernate polices bulk UPDATE statements at validation time. The setting hibernate.query.immutable_entity_update_query_handling_mode selects the reaction: WARNING logs, ALLOW logs at core level, and EXCEPTION (the default) throws HibernateException listing the entity's query spaces. Note the update also silently does nothing at runtime in some modes — the setting only controls detection messaging.","triggerScenarios":"Executing HQL 'UPDATE ImmutableEntity SET ...' or a CriteriaUpdate against an @Immutable entity while the handling mode is EXCEPTION (default). Also triggered by generic bulk-update helpers that sweep many entity types, some of which are immutable.","commonSituations":"Marking entities immutable for caching/read-model performance while legacy update code paths remain; tests that reuse production mappings with @Immutable value objects; teams enabling stricter handling modes after an audit.","solutions":["Remove the update path — an immutable entity is not supposed to change; delete-and-insert instead if the data must change","If the update is intentional and accepted, relax detection: set hibernate.query.immutable_entity_update_query_handling_mode=warning (or allow) in persistence.xml/spring.jpa.properties","If the entity really needs mutation, remove @Immutable / set mutable=true in the mapping and re-run your concurrency tests"],"exampleFix":"# before (default EXCEPTION)\n# UPDATE on an @Immutable entity throws HibernateException\n\n# after — accept the update deliberately\nspring.jpa.properties.hibernate.query.immutable_entity_update_query_handling_mode=warning\n\n// or better: remove the update code for immutable entities","handlingStrategy":"validation","validationCode":"// Check mutability before issuing a bulk update\nEntityType<?> et = emf.getMetamodel().entity(entityName);\nboolean immutable = et.getJavaType().isAnnotationPresent(org.hibernate.annotations.Immutable.class);\nif (immutable) throw new UnsupportedOperationException(\"Refusing to update immutable entity \" + entityName);","typeGuard":"static boolean isImmutableEntity(EntityManagerFactory emf, Class<?> type) {\n    return type.isAnnotationPresent(org.hibernate.annotations.Immutable.class);\n}","tryCatchPattern":"try { em.createQuery(update).executeUpdate(); } catch (HibernateException e) { if (e.getMessage().contains(\"immutable entity\")) { /* log config mistake and rethrow or skip */ } else throw e; }","preventionTips":["Grep for UPDATE paths (HQL and criteria) whenever you add @Immutable to a mapping","Keep a list of immutable entities and check it in generic bulk-update helpers","Decide the handling mode (warning/allow/exception) deliberately in configuration rather than relying on the default"],"tags":["hibernate","immutable-entity","bulk-update","configuration","hql"],"backgroundTag":"immutable-entity-update","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}