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
OptimisticForceIncrementLockingStrategy works by incrementing the entity's version column at commit; with no version column there is nothing to increment. Its constructor therefore throws HibernateException 'Entity '<name>' has no version and may not be locked at level <mode>' when lockable.isVersioned() is false, after first validating the lock mode. The exception appears the first time the strategy is constructed for that entity.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticForceIncrementLockingStrategy.java:41
public class OptimisticForceIncrementLockingStrategy implements LockingStrategy {
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 OptimisticForceIncrementLockingStrategy(EntityPersister lockable, LockMode lockMode) {
this.lockable = lockable;
this.lockMode = lockMode;
if ( lockMode.lessThan( LockMode.OPTIMISTIC_FORCE_INCREMENT ) ) {
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, EventSource session) {
// final EntityEntry entry = session.getPersistenceContextInternal().getEntry( object );
// Register the EntityIncrementVersionProcess action to run just prior to transaction commit.
session.getActionQueue().registerCallback( new EntityIncrementVersionProcess( object ) );
}
protected LockMode getLockMode() {
return lockMode;
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Add @Version private long version; (plus the version column in the schema) to the entity
- If a version column cannot be added, use pessimistic locking for that entity instead
- Verify with session.getMetamodel().entityPersister(clazz).isVersioned() before issuing OPTIMISTIC_FORCE_INCREMENT locks
- Check the exception message: it names the entity and requested level, making the offending mapping easy to locate
Example fix
// before
@Entity
public class Document {
@Id private Long id;
private String body;
}
// after
@Entity
public class Document {
@Id private Long id;
@Version private long version;
private String body;
} Defensive patterns
Strategy: validation
Validate before calling
EntityPersister persister = session.getEntityPersister(Document.class.getName(), document);
if (!persister.isVersioned()) {
throw new IllegalArgumentException(Document.class.getName() + " needs a @Version column for OPTIMISTIC_FORCE_INCREMENT");
}
session.lock(document, LockMode.OPTIMISTIC_FORCE_INCREMENT); Try / catch
try {
session.lock(document, LockMode.OPTIMISTIC_FORCE_INCREMENT);
}
catch (HibernateException e) {
if (e.getMessage().contains("has no version")) {
throw new IllegalStateException("Add @Version to " + document.getClass().getName(), e);
}
throw e;
} Prevention
- Every entity that will ever be force-increment locked must have a @Version field and column
- Keep entity templates with @Version included so new entities start versioned
- Add a metadata test listing entities locked optimistically and asserting each isVersioned()
When it happens
Trigger: session.lock(entity, LockMode.OPTIMISTIC_FORCE_INCREMENT) or jakarta lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT) on an entity without @Version; direct construction of OptimisticForceIncrementLockingStrategy with a persister whose isVersioned() is false. The constructor's second check fires immediately.
Common situations: Adding optimistic force-increment locking to a legacy entity model that never had version columns; new entities copied from templates without the version field; schema where the version column exists in DB but is not mapped (missing @Version annotation after refactor).
Related errors
- Entity '{}' has no version and may not be locked at level {}
- Entity '{}' has no version and may not be locked via 'update
- Entity '{}' has no version and may not be locked at level {}
- Property '${property}' is annotated '@OptimisticLock(exclude
- Class '" + propertyHolder.getEntityName() + "' is annotated
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/14f7394e40388f49.
Report an issue: GitHub.