hibernate/hibernate-orm · error · SemanticException

Increment option specified for update of non-versioned entit

Error message

Increment option specified for update of non-versioned entity

What it means

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.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java:911

							combinePredicates( queryNodeProcessingState.getPredicate(),
									additionalRestrictions ) ),
					emptyList()
			);
		}
		finally {
			popProcessingStateStack();
			this.currentSqmStatement = oldSqmStatement;
			this.cteContainer = oldCteContainer;
		}
	}

	public void addVersionedAssignment(Consumer<Assignment> assignmentConsumer, SqmUpdateStatement<?> sqmStatement) {
		if ( sqmStatement.isVersioned() ) {
			final var persister =
					getMappingMetamodel()
							.findEntityDescriptor( sqmStatement.getTarget().getEntityName() );
			if ( !persister.isVersioned() ) {
				throw new SemanticException( "Increment option specified for update of non-versioned entity" );
			}

			final var versionType = persister.getVersionType();
			if ( versionType instanceof UserVersionType ) {
				throw new SemanticException( "User-defined version types not supported for increment option" );
			}

			currentClauseStack.push( Clause.SET );
			final var versionMapping = persister.getVersionMapping();
			final var targetColumnReferences = BasicValuedPathInterpretation.from(
					(SqmBasicValuedSimplePath<?>)
							SqmExpressionHelper.get( sqmStatement.getRoot(),
									versionMapping.getPartName() ),
					this,
					jpaQueryComplianceEnabled
			).getColumnReferences();
			currentClauseStack.pop();
			assert targetColumnReferences.size() == 1;

View on GitHub (pinned to fad1729dce)

Solutions

  1. 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')
  2. Drop VERSIONED from the statement: 'UPDATE Person p SET p.name = :n' (bulk updates then skip version checks, as JPA specifies)
  3. If optimistic checking of bulk updates is required, use a version predicate manually: '... where p.version = :v' and set version = version + 1 explicitly

Example fix

-- before
UPDATE VERSIONED Person p SET p.name = :name
-- after (option 1: add version to entity)
@Version private long version;
-- after (option 2: drop the keyword)
UPDATE Person p SET p.name = :name
Defensive patterns

Strategy: validation

Validate before calling

// Verify the entity is versioned before issuing UPDATE VERSIONED
boolean versioned = em.getMetamodel().entity(Person.class)
        .getSingularAttributes().stream().anyMatch(SingularAttribute::isVersion);
if (!versioned) throw new IllegalStateException("Entity has no @Version; remove VERSIONED keyword");

Type guard

static boolean isVersioned(EntityManager em, Class<?> entity) {
    return em.getMetamodel().entity(entity).getSingularAttributes().stream()
            .anyMatch(jakarta.persistence.metamodel.SingularAttribute::isVersion);
}

Try / catch

catch (SemanticException e) { if (e.getMessage().contains("non-versioned entity")) { /* add @Version + migration, or drop VERSIONED */ } else throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/f347f83a8d5f3ea5. Report an issue: GitHub.