hibernate/hibernate-orm · error · HibernateException

Lock mode ${lockMode} not valid for locking via 'update' sta

Error message

Lock mode ${lockMode} not valid for locking via 'update' statement

What it means

AbstractPessimisticUpdateLockingStrategy implements pessimistic locking by issuing an UPDATE ... where id=? and version=? against the row. Such an update can only express a write-level lock, so the constructor rejects any LockMode weaker than PESSIMISTIC_READ with HibernateException 'Lock mode <mode> not valid for locking via 'update' statement'. The strategy is built by dialects whose getLockingStrategy maps lock modes to update-based locking.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/dialect/lock/AbstractPessimisticUpdateLockingStrategy.java:42

 */
public abstract class AbstractPessimisticUpdateLockingStrategy implements LockingStrategy {

	private final EntityPersister lockable;
	private final LockMode lockMode;
	private final String sql;

	/**
	 * Construct a locking strategy based on SQL UPDATE statements.
	 *
	 * @param lockable The metadata for the entity to be locked.
	 * @param lockMode Indicates the type of lock to be acquired.  Note that
	 * read-locks are not valid for this strategy.
	 */
	public AbstractPessimisticUpdateLockingStrategy(EntityPersister lockable, LockMode lockMode) {
		this.lockable = lockable;
		this.lockMode = lockMode;
		if ( lockMode.lessThan( LockMode.PESSIMISTIC_READ ) ) {
			throw new HibernateException( "Lock mode " + lockMode
						+ " not valid for locking via 'update' statement" );
		}
		if ( !lockable.isVersioned() ) {
			throw new HibernateException( "Entity '" + lockable.getEntityName()
						+ "' has no version and may not be locked via 'update' statement" );
		}
		this.sql = generateLockString();
	}

	@Override
	public void lock(Object id, Object version, Object object, int timeout, SharedSessionContractImplementor session) {
		try {
			doLock( id, version, session );
		}
		catch (JDBCException e) {
			throw new PessimisticEntityLockException( object, "Could not obtain pessimistic lock", e );
		}
	}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Request PESSIMISTIC_READ or stronger (usually PESSIMISTIC_WRITE) when the dialect uses update-based locking
  2. If you maintain a custom dialect, return the update strategy only for PESSIMISTIC_READ/PESSIMISTIC_WRITE/PESSIMISTIC_FORCE_INCREMENT and a select-based strategy otherwise
  3. Replace deprecated LockMode.READ/UPGRADE usages with their modern equivalents that the dialect maps correctly
  4. Prefer optimistic (@Version-based) locking for read intents instead of forcing weak pessimistic modes

Example fix

// before
session.buildLockRequest(new LockOptions(LockMode.READ)).lock(person);

// after
session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(person);
Defensive patterns

Strategy: validation

Validate before calling

// Only request modes the strategy accepts
static LockMode sanitizeForUpdateLocking(LockMode requested) {
    return requested.lessThan(LockMode.PESSIMISTIC_READ) ? LockMode.PESSIMISTIC_WRITE : requested;
}

session.buildLockRequest(new LockOptions(sanitizeForUpdateLocking(LockMode.READ))).lock(person);

Try / catch

try {
    session.buildLockRequest(new LockOptions(mode)).lock(person);
}
catch (HibernateException e) {
    if (e.getMessage().contains("not valid for locking via 'update' statement")) {
        throw new IllegalArgumentException("Use PESSIMISTIC_READ or stronger with update-based locking (got " + mode + ")", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A dialect returns an update-based locking strategy for a weak lock mode and Hibernate constructs the strategy - e.g. session.buildLockRequest(new LockOptions(LockMode.READ)).lock(entity) or session.lock(entity, LockMode.OPTIMISTIC) - while the active dialect uses AbstractPessimisticUpdateLockingStrategy for that mode (lockMode.lessThan(PESSIMISTIC_READ) is true). Custom dialects overriding getLockingStrategy too broadly hit this immediately at strategy construction.

Common situations: Custom dialect implementations that return UpdateLockingStrategy for every requested LockMode; legacy code using old LockMode constants (READ/UPGRADE, now NONE/OPTIMISTIC) on databases where Hibernate emulates locking via UPDATE; upgrading Hibernate versions where deprecated lock modes map differently.

Related errors


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