hibernate/hibernate-orm · error · HibernateException
Entity '{}' may not be locked at level {}
Error message
Entity '{}' may not be locked at level {} What it means
OptimisticLockingStrategy verifies the entity version at transaction commit and is only valid for lock modes at or above LockMode.OPTIMISTIC. The constructor throws HibernateException 'Entity '<name>' may not be locked at level <mode>' when lockMode.lessThan(LockMode.OPTIMISTIC), i.e. for NONE/READ-style modes. Dialects create this strategy from getLockingStrategy, so the failure indicates a weaker mode was routed here.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/dialect/lock/OptimisticLockingStrategy.java:36
*
* @author Scott Marlow
* @since 3.5
*/
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
- Use LockMode.OPTIMISTIC (JPA: LockModeType.OPTIMISTIC) or stronger with this strategy
- Update custom dialect getLockingStrategy mappings so weak modes never reach OptimisticLockingStrategy
- Replace deprecated LockMode constants (READ, UPGRADE) with their modern equivalents across the code base
Example fix
// before session.lock(person, LockMode.READ); // after session.lock(person, LockMode.OPTIMISTIC);
Defensive patterns
Strategy: validation
Validate before calling
// Ensure the mode is at least OPTIMISTIC before locking
static LockMode atLeastOptimistic(LockMode m) {
return m.lessThan(LockMode.OPTIMISTIC) ? LockMode.OPTIMISTIC : m;
}
session.lock(person, atLeastOptimistic(requestedMode)); Try / catch
try {
session.lock(person, LockMode.OPTIMISTIC);
}
catch (HibernateException e) {
if (e.getMessage().contains("may not be locked at level")) {
throw new IllegalArgumentException("Use LockMode.OPTIMISTIC or stronger (got a weaker/legacy mode)", e);
}
throw e;
} Prevention
- Replace legacy LockMode.READ/UPGRADE with OPTIMISTIC/PESSIMISTIC_WRITE across the code base
- In custom dialect getLockingStrategy, never route modes below OPTIMISTIC to OptimisticLockingStrategy
- Run a migration lint (grep for LockMode.READ/UPGRADE) during Hibernate major upgrades
When it happens
Trigger: session.lock(entity, LockMode.READ) (legacy constant) or LockMode.NONE with a dialect that resolves those modes to OptimisticLockingStrategy; explicit new OptimisticLockingStrategy(persister, LockMode.READ) in custom code. Thrown eagerly in the constructor at strategy construction time.
Common situations: Old code bases still using pre-JPA LockMode.READ/UPGRADE constants after a Hibernate upgrade changed mode mappings; custom dialects whose getLockingStrategy switch statement was copied from an old version; direct strategy instantiation in tests.
Related errors
- Entity '{}' may not be locked at level {}
- Lock mode ${lockMode} not valid for locking via 'update' sta
- Entity '{}' may not be locked at level {}
- WRITE is not a valid LockMode as an argument
- Optimistic locking strategies not supported in stateless ses
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/98f3c08a67506a1a.
Report an issue: GitHub.