hibernate/hibernate-orm · error · SemanticException
User-defined version types not supported for increment optio
Error message
User-defined version types not supported for increment option
What it means
For 'UPDATE VERSIONED', Hibernate increments the version column arithmetically in SQL (version = version + 1) when the version is numeric. If the version type is a UserVersionType (a custom user-supplied version strategy), Hibernate cannot derive a SQL increment expression for it, so addVersionedAssignment throws SemanticException('User-defined version types not supported for increment option'). The entity is versioned, but its version is user-defined, which blocks the bulk increment path.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java:916
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;
final var versionColumn = targetColumnReferences.get( 0 );
final var value =
versionMapping.getJdbcMapping().getJdbcType().isTemporal()
? new VersionTypeSeedParameterSpecification( versionMapping )View on GitHub (pinned to fad1729dce)
Solutions
- Switch the version to a built-in incrementable type: @Version private long version; or @Version private Instant/LocalDateTime/... timestamp
- Remove VERSIONED from the bulk update and manage the custom version yourself: set version = :newVersion and filter where version = :oldVersion in the same statement
- Implement the increment through a custom SQL update (session.createNativeMutationQuery) where you control the version expression
- If the custom type merely wraps a number, replace it with the plain numeric field and convert at the boundaries
Example fix
-- before
@Entity class Doc { @Version MyStringVersion v; } // UserVersionType
UPDATE VERSIONED Doc d SET d.title = :t
-- after
@Entity class Doc { @Version long v; }
UPDATE VERSIONED Doc d SET d.title = :t Defensive patterns
Strategy: validation
Validate before calling
// Reject VERSIONED updates when the version type is user-defined
EntityType<?> et = em.getMetamodel().entity(Doc.class);
SingularAttribute<?, ?> ver = et.getSingularAttributes().stream()
.filter(SingularAttribute::isVersion).findFirst().orElseThrow();
// if a custom UserType backs it, require the non-VERSIONED form Type guard
static boolean builtInVersion(EntityManager em, Class<?> c) {
Class<?> jt = em.getMetamodel().entity(c).getSingularAttributes().stream()
.filter(a -> a.isVersion()).findFirst().map(a -> a.getJavaType()).orElse(null);
return Number.class.isAssignableFrom(jt) || java.util.Date.class.isAssignableFrom(jt)
|| java.time.temporal.Temporal.class.isAssignableFrom(jt);
} Try / catch
catch (SemanticException e) { if (e.getMessage().contains("User-defined version")) { /* drop VERSIONED and increment manually */ } else throw e; } Prevention
- Prefer long/timestamp version columns over UserVersionType
- If a custom version type is mandatory, never use the VERSIONED keyword
- Replace legacy UserVersionTypes during Hibernate upgrades
When it happens
Trigger: Entity mapped with @Version on a field whose type is resolved through a custom UserType implementing UserVersionType (e.g. a legacy string GUID-based version, custom timestamp), then executing 'UPDATE VERSIONED ...'; reusing a legacy Hibernate 5 custom version type after upgrading to 6/7 where addVersionedAssignment was reworked to SQL-side increments.
Common situations: Legacy applications with custom version strategies (homegrown OptimisticLockStyle types); migrating from Hibernate 5 where versioned bulk updates with user types were handled differently; teams adding the new standardized VERSIONED keyword (JPA 3.2) to entities that predate built-in version types.
Related errors
- Increment option specified for update of non-versioned entit
- 'generated' attribute cannot be 'insert' or 'update' for ver
- Entity '${persister.getEntityName()}' has no version and may
- The query attempts to update an immutable entity: ${querySpa
- Insert conflict 'do update' clause with constraint name is n
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/f31219bfcd1033a0.
Report an issue: GitHub.