{"record":{"id":"f347f83a8d5f3ea5","repo":"hibernate/hibernate-orm","slug":"increment-option-specified-for-update-of-non-versi","errorCode":null,"errorMessage":"Increment option specified for update of non-versioned entity","messagePattern":"Increment option specified for update of non-versioned entity","errorType":"exception","errorClass":"SemanticException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java","lineNumber":911,"sourceCode":"\t\t\t\t\t\t\tcombinePredicates( queryNodeProcessingState.getPredicate(),\n\t\t\t\t\t\t\t\t\tadditionalRestrictions ) ),\n\t\t\t\t\temptyList()\n\t\t\t);\n\t\t}\n\t\tfinally {\n\t\t\tpopProcessingStateStack();\n\t\t\tthis.currentSqmStatement = oldSqmStatement;\n\t\t\tthis.cteContainer = oldCteContainer;\n\t\t}\n\t}\n\n\tpublic void addVersionedAssignment(Consumer<Assignment> assignmentConsumer, SqmUpdateStatement<?> sqmStatement) {\n\t\tif ( sqmStatement.isVersioned() ) {\n\t\t\tfinal var persister =\n\t\t\t\t\tgetMappingMetamodel()\n\t\t\t\t\t\t\t.findEntityDescriptor( sqmStatement.getTarget().getEntityName() );\n\t\t\tif ( !persister.isVersioned() ) {\n\t\t\t\tthrow new SemanticException( \"Increment option specified for update of non-versioned entity\" );\n\t\t\t}\n\n\t\t\tfinal var versionType = persister.getVersionType();\n\t\t\tif ( versionType instanceof UserVersionType ) {\n\t\t\t\tthrow new SemanticException( \"User-defined version types not supported for increment option\" );\n\t\t\t}\n\n\t\t\tcurrentClauseStack.push( Clause.SET );\n\t\t\tfinal var versionMapping = persister.getVersionMapping();\n\t\t\tfinal var targetColumnReferences = BasicValuedPathInterpretation.from(\n\t\t\t\t\t(SqmBasicValuedSimplePath<?>)\n\t\t\t\t\t\t\tSqmExpressionHelper.get( sqmStatement.getRoot(),\n\t\t\t\t\t\t\t\t\tversionMapping.getPartName() ),\n\t\t\t\t\tthis,\n\t\t\t\t\tjpaQueryComplianceEnabled\n\t\t\t).getColumnReferences();\n\t\t\tcurrentClauseStack.pop();\n\t\t\tassert targetColumnReferences.size() == 1;","sourceCodeStart":893,"sourceCodeEnd":929,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java#L893-L929","documentation":"HQL/JPA supports 'UPDATE [VERSIONED] Entity ... SET ...' where VERSIONED asks Hibernate to bump the optimistic-lock version inside the bulk update SQL. BaseSqmToSqlAstConverter.addVersionedAssignment first verifies the target entity descriptor actually has a version; if persister.isVersioned() is false it throws SemanticException('Increment option specified for update of non-versioned entity'). The query is rejected at translation time - a version increment cannot be synthesized for an entity with no @Version attribute.","triggerScenarios":"HQL 'UPDATE VERSIONED Person p SET p.name = :n' when Person has no @Version field; Criteria update with versioned semantics enabled (HibernateCriteriaBuilder.update(...)+versioned) against a non-versioned entity; copying the VERSIONED keyword from another entity's working update statement.","commonSituations":"Enabling bulk optimistic locking on legacy entities that never had a version column; adding VERSIONED while testing and forgetting to add the @Version mapping/migration; generated update statements that always emit VERSIONED; upgrading to JPA 3.2/Hibernate 7 where UPDATE VERSIONED became standardized HQL.","solutions":["Add a version attribute to the entity: @Version private long version; plus a DB column (flyway/liquibase migration 'alter table person add column version bigint not null default 0')","Drop VERSIONED from the statement: 'UPDATE Person p SET p.name = :n' (bulk updates then skip version checks, as JPA specifies)","If optimistic checking of bulk updates is required, use a version predicate manually: '... where p.version = :v' and set version = version + 1 explicitly"],"exampleFix":"-- before\nUPDATE VERSIONED Person p SET p.name = :name\n-- after (option 1: add version to entity)\n@Version private long version;\n-- after (option 2: drop the keyword)\nUPDATE Person p SET p.name = :name","handlingStrategy":"validation","validationCode":"// Verify the entity is versioned before issuing UPDATE VERSIONED\nboolean versioned = em.getMetamodel().entity(Person.class)\n        .getSingularAttributes().stream().anyMatch(SingularAttribute::isVersion);\nif (!versioned) throw new IllegalStateException(\"Entity has no @Version; remove VERSIONED keyword\");","typeGuard":"static boolean isVersioned(EntityManager em, Class<?> entity) {\n    return em.getMetamodel().entity(entity).getSingularAttributes().stream()\n            .anyMatch(jakarta.persistence.metamodel.SingularAttribute::isVersion);\n}","tryCatchPattern":"catch (SemanticException e) { if (e.getMessage().contains(\"non-versioned entity\")) { /* add @Version + migration, or drop VERSIONED */ } else throw e; }","preventionTips":["Add @Version to entities before adopting UPDATE VERSIONED","Generate a DDL migration whenever adding a version column","Keep one canonical bulk-update builder that checks metamodel versioning"],"tags":["hibernate","hql","bulk-update","optimistic-locking","version","jpql"],"backgroundTag":"missing-version-attribute","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}