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
- Use a regular stateful Session/EntityManager wherever optimistic locking is required
- For batch workloads that must stay stateless, switch to explicit pessimistic locks (select for update) or manual version checks via conditional UPDATE
- Guard shared code with 'session instanceof EventSource' and choose the locking approach per session type
- 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
- Never issue optimistic lock requests through StatelessSession - it has no action queue for version verification
- Audit lock calls before migrating batch jobs to StatelessSession
- Encapsulate session acquisition so optimistic-locking code paths always receive a stateful Session
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
- Entity '{}' may not be locked at level {}
- Entity '{}' has no version and may not be locked at level {}
- Entity '{}' may not be locked at level {}
- Entity '{}' has no version and may not be locked at level {}
- 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/202eda521847f549.
Report an issue: GitHub.