hibernate/hibernate-orm · error · UnsupportedOperationException

Optimistic locking strategies not supported in stateless ses

Error message

Optimistic locking strategies not supported in stateless session

What it means

LockingStrategy's deprecated default lock(id, version, object, int timeout, session) method only supports regular stateful sessions: it tests whether the session is an EventSource, and otherwise throws UnsupportedOperationException 'Optimistic locking strategies not supported in stateless session'. Optimistic strategies (version verify/increment at commit) rely on the persistence context and action queue that a StatelessSession does not have, hence the hard refusal. The newer Timeout overload delegates to this default method, so it inherits the restriction.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/dialect/lock/LockingStrategy.java:72

	 * @param version The current version (or null if not versioned)
	 * @param object The object logically being locked (currently not used)
	 * @param timeout timeout in milliseconds, 0 = no wait, -1 = wait indefinitely
	 * @param session The session from which the lock request originated
	 *
	 * @throws StaleObjectStateException Indicates an inability to locate the database row as part of acquiring
	 * the requested lock.
	 * @throws LockingStrategyException Indicates a failure in the lock attempt

	 * @deprecated Use {@link #lock(Object, Object, Object, Timeout, SharedSessionContractImplementor)}
	 */
	@Deprecated(since = "7.1")
	default void lock(Object id, Object version, Object object, int timeout, SharedSessionContractImplementor session)
			throws StaleObjectStateException, LockingStrategyException {
		if ( session instanceof EventSource eventSource ) {
			lock( id, version, object, timeout, eventSource );
		}
		else {
			throw new UnsupportedOperationException( "Optimistic locking strategies not supported in stateless session" );
		}
	}

	default void lock(Object id, Object version, Object object, Timeout timeout, SharedSessionContractImplementor session)
			throws StaleObjectStateException, LockingStrategyException {
		lock( id, version, object, timeout.milliseconds(), session );
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use a regular stateful Session/EntityManager wherever optimistic locking is required
  2. For batch workloads that must stay stateless, switch to explicit pessimistic locks (select for update) or manual version checks via conditional UPDATE
  3. Guard shared code with 'session instanceof EventSource' and choose the locking approach per session type
  4. Audit for LockMode.OPTIMISTIC / OPTIMISTIC_FORCE_INCREMENT usages before introducing StatelessSession

Example fix

// before
StatelessSession s = sessionFactory.openStatelessSession();
s.lock(person, LockMode.OPTIMISTIC);

// after
Session s = sessionFactory.openSession();
// optimistic locking works via the action queue on commit
s.lock(person, LockMode.OPTIMISTIC);
Defensive patterns

Strategy: fallback

Validate before calling

// Route locking by session type before calling lock
if (session instanceof org.hibernate.engine.spi.SessionImplementor) {
    session.lock(person, LockMode.OPTIMISTIC); // stateful: OK
}
else {
    // StatelessSession: optimistic strategies unsupported - use explicit versioned UPDATE instead
    int updated = statelessSession.createMutationQuery(
            "update Person p set p.version = p.version + 1 where p.id = :id and p.version = :v")
            .setParameter("id", id).setParameter("v", version)
            .executeUpdate();
    if (updated == 0) throw new OptimisticLockException(person);
}

Try / catch

try {
    session.lock(person, LockMode.OPTIMISTIC);
}
catch (UnsupportedOperationException e) {
    if (e.getMessage().contains("stateless session")) {
        throw new IllegalStateException("Open a stateful Session for optimistic locking", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling session.lock(...)/buildLockRequest(...) with an optimistic lock mode on a StatelessSession obtained via sessionFactory.openStatelessSession(), where the resolved LockingStrategy falls back to this default method; also direct strategy invocations passing a non-EventSource SharedSessionContractImplementor. Deprecated since 7.1 in favor of the Timeout overload.

Common situations: Bulk-processing code using StatelessSession for throughput that later adds session.lock(entity, LockMode.OPTIMISTIC) for consistency; shared service code receiving either session type; migrating batch jobs from Session to StatelessSession without auditing locking calls.

Related errors


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