hibernate/hibernate-orm · error · HibernateException
Cannot force version increment relative to subtype; use the
Error message
Cannot force version increment relative to subtype; use the root type
What it means
UpdateCoordinatorStandard.forceVersionIncrement(id, currentVersion, nextVersion, session) executes the dedicated version-update mutation group. For subtype persisters of a JOINED hierarchy that group is null, because the version column lives in the root table, so the coordinator refuses with HibernateException 'Cannot force version increment relative to subtype; use the root type'. The increment must be routed through the root persister, which owns the version column.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java:137
return versionUpdateGroup;
}
protected BatchKey getBatchKey() {
return batchKey;
}
public final boolean isModifiableEntity(EntityEntry entry) {
return entry == null ? entityPersister().isMutable() : entry.isModifiableEntity();
}
@Override
public void forceVersionIncrement(
Object id,
Object currentVersion,
Object nextVersion,
SharedSessionContractImplementor session) {
if ( versionUpdateGroup == null ) {
throw new HibernateException( "Cannot force version increment relative to subtype; use the root type" );
}
doVersionUpdate( null, id, nextVersion, currentVersion, getLoadedState( id, session ), session );
}
private @Nullable Object[] getLoadedState(Object id, SharedSessionContractImplementor session) {
return entityPersister.hasPartitionedSelectionMapping()
? session.getPersistenceContextInternal()
.getEntityHolder( session.generateEntityKey( id, entityPersister ) ).getEntityEntry().getLoadedState()
: null;
}
@Override
public void forceVersionIncrement(
Object id,
Object currentVersion,
Object nextVersion,
boolean batching,
SharedSessionContractImplementor session) {View on GitHub (pinned to fad1729dce)
Solutions
- Force the increment through the root type: operate on the root persister / load and lock the row as the root entity
- Use a bulk versioned JPQL update on the root table (UPDATE Root SET version = version + 1 WHERE id = :id)
- Upgrade Hibernate - JoinedSubclassEntityPersister delegates forceVersionIncrement to the super mapping type; ensure your version includes that delegation
- Guard call sites so force-increment is only issued when the persister is the root mapping type
Example fix
// before: force-increment reaches the subtype persister
session.buildLockRequest(LockOptions.forceVersion()).lock(customer); // Customer extends Person (JOINED)
// after: go through the root type
session.buildLockRequest(LockOptions.forceVersion()).lock(personRoot); // lock as Person
// or via bulk update on the root table:
// session.createQuery("update Person p set p.version = p.version + 1 where p.id = :id") Defensive patterns
Strategy: validation
Validate before calling
// only force-increment through root persisters
EntityPersister p = session.getEntityPersister(entity.getClass().getName(), entity);
if (p.getSuperMappingType() != null) {
// subtype of a JOINED hierarchy: increment via the root type or a bulk update instead
} Try / catch
try { session.buildLockRequest(LockOptions.forceVersion()).lock(entity); } catch (HibernateException e) { if (e.getMessage().contains("relative to subtype")) { /* re-issue the lock through the root entity type */ } throw e; } Prevention
- Apply OPTIMISTIC_FORCE_INCREMENT through the hierarchy root, never through subtype references
- Wrap force-increment helpers in a root-type check (getSuperMappingType() == null)
- Test locking paths for every level of an inheritance hierarchy
When it happens
Trigger: session.buildLockRequest(LockOptions.forceVersion()).lock(entity) or EntityManager.lock(entity, LockModeType.OPTIMISTIC_FORCE_INCREMENT) where the instance's entity name resolves to a joined subclass rather than the hierarchy root; force-increment paths that reach the subtype's update coordinator directly instead of delegating upward.
Common situations: Version columns defined on the root of JOINED hierarchies; framework code that auto-force-increments versions on change (audit hooks, Spring lock utilities) and receives subtype instances; upgrades where delegation order in JoinedSubclassEntityPersister changed.
Related errors
- Entity '${persister.getEntityName()}' has no version and may
- Entity '{name}' has 'OptimisticLockType.{optimisticLockStyle
- Discriminator formulas on joined inheritance hierarchies not
- Could not format discriminator value to SQL string
- optimistic-lock=all|dirty not supported for joined-subclass
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/02b5498fd484f6b0.
Report an issue: GitHub.