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

  1. Use LockMode.OPTIMISTIC (JPA: LockModeType.OPTIMISTIC) or stronger with this strategy
  2. Update custom dialect getLockingStrategy mappings so weak modes never reach OptimisticLockingStrategy
  3. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/98f3c08a67506a1a. Report an issue: GitHub.