hibernate/hibernate-orm · error · HibernateException
Entity '{}' has no version and may not be locked at level {}
Error message
Entity '{}' has no version and may not be locked at level {} What it means
Like all version-increment strategies, PessimisticForceIncrementLockingStrategy requires the entity to be versioned because its lock action is 'bump the version now'. After checking the lock mode, the constructor throws HibernateException 'Entity '<name>' has no version and may not be locked at level <mode>' when lockable.isVersioned() is false. The message identifies both the entity and the lock level requested, so the offending mapping is easy to find.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticForceIncrementLockingStrategy.java:41
private final EntityPersister lockable;
private final LockMode lockMode;
/**
* Construct locking strategy.
*
* @param lockable The metadata for the entity to be locked.
* @param lockMode Indicates the type of lock to be acquired.
*/
public PessimisticForceIncrementLockingStrategy(EntityPersister lockable, LockMode lockMode) {
this.lockable = lockable;
this.lockMode = lockMode;
// ForceIncrement can be used for PESSIMISTIC_READ, PESSIMISTIC_WRITE or PESSIMISTIC_FORCE_INCREMENT
if ( lockMode.lessThan( LockMode.PESSIMISTIC_READ ) ) {
throw new HibernateException( "Entity '" + lockable.getEntityName()
+ "' may not be locked at level " + lockMode );
}
if ( !lockable.isVersioned() ) {
throw new HibernateException( "Entity '" + lockable.getEntityName()
+ "' has no version and may not be locked at level " + lockMode);
}
}
@Override
public void lock(Object id, Object version, Object object, int timeout, SharedSessionContractImplementor session) {
final var entry = session.getPersistenceContextInternal().getEntry( object );
OptimisticLockHelper.forceVersionIncrement( object, entry, session );
}
/**
* Retrieve the specific lock mode defined.
*
* @return The specific lock mode.
*/
protected LockMode getLockMode() {
return lockMode;
}View on GitHub (pinned to fad1729dce)
Solutions
- Add @Version (and the DB column) to the entity being force-increment locked
- Use plain PESSIMISTIC_WRITE (select/update-based lock without forced increment) if adding a version is not possible
- Add an isVersioned() guard to shared locking utilities and fail with a clear domain-specific message
Example fix
// before
@Entity
public class Counter {
@Id private Long id;
private long value;
}
// after
@Entity
public class Counter {
@Id private Long id;
@Version private long version;
private long value;
} Defensive patterns
Strategy: validation
Validate before calling
EntityPersister persister = session.getEntityPersister(Counter.class.getName(), counter);
if (!persister.isVersioned()) {
throw new IllegalArgumentException(Counter.class.getName() + " needs @Version for forced version increment");
}
session.lock(counter, LockMode.PESSIMISTIC_FORCE_INCREMENT); Try / catch
try {
session.lock(counter, LockMode.PESSIMISTIC_FORCE_INCREMENT);
}
catch (HibernateException e) {
if (e.getMessage().contains("has no version")) {
throw new IllegalStateException("Add @Version to " + counter.getClass().getName() + " - force-increment needs a version to bump", e);
}
throw e;
} Prevention
- Force-increment locking literally increments @Version - no version, no lock
- Include @Version in the entity template used by code generators
- When applying a blanket locking policy, exempt or upgrade unversioned entities explicitly
When it happens
Trigger: session.lock(entity, LockMode.PESSIMISTIC_FORCE_INCREMENT) or EntityManager.lock(entity, LockModeType.PESSIMISTIC_FORCE_INCREMENT) on an entity without @Version; direct new PessimisticForceIncrementLockingStrategy(persister, mode) where the persister reports no version. Fires on first lock (strategy construction).
Common situations: Applying a uniform 'lock before update' policy across all entities, some of which predate the policy and lack version fields; read-only/reporting entities drawn into transactional flows; version column dropped in the schema but mapping not updated.
Related errors
- Entity '{}' has no version and may not be locked via 'update
- Entity '{}' has no version and may not be locked at level {}
- Entity '{}' has no version and may not be locked at level {}
- Spanner does not support no wait.
- Spanner does not support skip locked.
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/f3f3609789e064a0.
Report an issue: GitHub.