hibernate/hibernate-orm · error · HibernateException
Entity '{}' may not be locked at level {}
Error message
Entity '{}' may not be locked at level {} What it means
PessimisticForceIncrementLockingStrategy immediately forces a version increment while also acting as a pessimistic lock; it accepts PESSIMISTIC_READ, PESSIMISTIC_WRITE and PESSIMISTIC_FORCE_INCREMENT. The constructor throws HibernateException 'Entity '<name>' may not be locked at level <mode>' when lockMode.lessThan(LockMode.PESSIMISTIC_READ), i.e. for OPTIMISTIC or weaker modes routed here. The comment in source spells out the three valid modes.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/dialect/lock/PessimisticForceIncrementLockingStrategy.java:37
* @author Scott Marlow
* @since 3.5
*/
public class PessimisticForceIncrementLockingStrategy 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 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.View on GitHub (pinned to fad1729dce)
Solutions
- Request PESSIMISTIC_READ, PESSIMISTIC_WRITE or PESSIMISTIC_FORCE_INCREMENT with this strategy
- Fix custom dialect mode-to-strategy mapping so weaker modes get optimistic or select-based strategies instead
- Use LockModeType JPA constants to make the intended strength explicit at call sites
Example fix
// before session.lock(person, LockMode.OPTIMISTIC); // routed to pessimistic force-increment strategy // after session.lock(person, LockMode.PESSIMISTIC_FORCE_INCREMENT);
Defensive patterns
Strategy: validation
Validate before calling
// Force the mode into the accepted range for force-increment locking
static LockMode atLeastPessimisticRead(LockMode m) {
return m.lessThan(LockMode.PESSIMISTIC_READ) ? LockMode.PESSIMISTIC_WRITE : m;
}
session.lock(person, atLeastPessimisticRead(requestedMode)); Try / catch
try {
session.lock(person, LockMode.PESSIMISTIC_FORCE_INCREMENT);
}
catch (HibernateException e) {
if (e.getMessage().contains("may not be locked at level")) {
throw new IllegalArgumentException("Use PESSIMISTIC_READ/PESSIMISTIC_WRITE/PESSIMISTIC_FORCE_INCREMENT", e);
}
throw e;
} Prevention
- Remember the valid set for PessimisticForceIncrementLockingStrategy: PESSIMISTIC_READ, PESSIMISTIC_WRITE, PESSIMISTIC_FORCE_INCREMENT
- Never map optimistic or weaker modes to force-increment strategies in custom dialects
- Prefer the JPA LockModeType constants so intent is explicit
When it happens
Trigger: A dialect's getLockingStrategy (or explicit code) hands this strategy a mode below PESSIMISTIC_READ - e.g. session.lock(entity, LockMode.OPTIMISTIC) resolving to PessimisticForceIncrementLockingStrategy, or direct construction with LockMode.UPGRADE. Thrown eagerly in the constructor with entity name and offending mode.
Common situations: Custom dialect getLockingStrategy implementations that map all modes to one strategy class; switching lock constants during a Hibernate major upgrade where LockMode ordering changed; test code constructing strategies with arbitrary modes.
Related errors
- Lock mode ${lockMode} not valid for locking via 'update' sta
- Entity '{}' may not be locked at level {}
- Entity '{}' may not be locked at level {}
- Lock mode {} not valid for locking via 'update' statement
- WRITE is not a valid LockMode as an argument
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/42794f1cd3201f2c.
Report an issue: GitHub.