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
OptimisticLockingStrategy ends its constructor validation by requiring a version attribute: optimistic locking is implemented as a version check at commit, so an unversioned entity cannot be protected. When lockable.isVersioned() returns false it throws HibernateException 'Entity '<name>' has no version and may not be locked at level <mode>', with the requested lock mode in the message. The strategy is created lazily, typically when the first lock request for the entity resolves the strategy.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticLockingStrategy.java:40
public class OptimisticLockingStrategy 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 OptimisticLockingStrategy(EntityPersister lockable, LockMode lockMode) {
this.lockable = lockable;
this.lockMode = lockMode;
if ( lockMode.lessThan( LockMode.OPTIMISTIC ) ) {
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) {
// Register the EntityVerifyVersionProcess action to run just prior to transaction commit.
session.getActionQueue().registerCallback( new EntityVerifyVersionProcess( object ) );
}
protected LockMode getLockMode() {
return lockMode;
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Add a @Version field and matching database column to the entity
- Use pessimistic locking (PESSIMISTIC_READ/PESSIMISTIC_WRITE) for unversioned entities
- Guard generic locking code with a persister.isVersioned() check and choose the strategy accordingly
Example fix
// before
@Entity
public class Account {
@Id private Long id;
private BigDecimal balance;
}
// after
@Entity
public class Account {
@Id private Long id;
@Version private long version;
private BigDecimal balance;
} Defensive patterns
Strategy: validation
Validate before calling
boolean versioned = session.getMetamodel().entityPersister(Account.class.getName()).isVersioned();
if (!versioned) {
throw new IllegalArgumentException(Account.class.getName() + " requires @Version for optimistic locking");
}
session.lock(account, LockMode.OPTIMISTIC); Try / catch
try {
session.lock(account, LockMode.OPTIMISTIC);
}
catch (HibernateException e) {
if (e.getMessage().contains("has no version")) {
throw new IllegalStateException("Add @Version to " + account.getClass().getName() + " or use pessimistic locking", e);
}
throw e;
} Prevention
- Add @Version to any entity that can be locked optimistically; it is cheap insurance
- In generic locking aspects, branch on persister.isVersioned() and pick pessimistic locking for unversioned types
- Watch inheritance: @Version must sit on the mapped entity class itself, not a non-persistent superclass or sibling
When it happens
Trigger: session.lock(entity, LockMode.OPTIMISTIC) or EntityManager.lock(entity, LockModeType.OPTIMISTIC) on an entity class lacking a @Version attribute; direct new OptimisticLockingStrategy(persister, LockMode.OPTIMISTIC) against an unversioned persister. The second constructor check fires with the entity name and lock level in the message.
Common situations: Entities created without optimistic locking in mind that later get locked by generic audit/locking aspects; inheritance hierarchies where the version was put on a sibling class; @Version removed accidentally during mapping refactor or moved to a non-persistent field.
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/59aea4ac7e46b21c.
Report an issue: GitHub.