hibernate/hibernate-orm · error · IllegalQueryOperationException
Locking with set operators is not supported!
Error message
Locking with set operators is not supported!
What it means
TimesTen supports row locks with aggregates but not with set operators (union/intersect/except). When a locking clause is required while a QueryGroup sits on the query-part stack and follow-on locking is DISALLOWed, determineLockingStrategy throws IllegalQueryOperationException: TimesTen cannot attach a lock to a set operation and the follow-on fallback is forbidden.
Source
Thrown at hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/TimesTenSqlAstTranslator.java:52
public TimesTenSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
super( sessionFactory, statement );
}
@Override
protected LockStrategy determineLockingStrategy(
QuerySpec querySpec,
Locking.FollowOn followOnStrategy) {
if ( followOnStrategy == Locking.FollowOn.FORCE ) {
return LockStrategy.FOLLOW_ON;
}
// TimesTen supports locks with aggregates but not with set operators
// See https://docs.oracle.com/cd/E11882_01/timesten.112/e21642/state.htm#TTSQL329
LockStrategy strategy = LockStrategy.CLAUSE;
if ( getQueryPartStack().findCurrentFirst( part -> part instanceof QueryGroup ? part : null ) != null ) {
if ( followOnStrategy == Locking.FollowOn.DISALLOW ) {
throw new IllegalQueryOperationException( "Locking with set operators is not supported!" );
}
else if ( followOnStrategy != Locking.FollowOn.IGNORE ) {
strategy = LockStrategy.NONE;
}
else {
strategy = LockStrategy.FOLLOW_ON;
}
}
return strategy;
}
@Override
protected void visitSqlSelections(SelectClause selectClause) {
renderRowsToClause( (QuerySpec) getQueryPartStack().getCurrent() );
super.visitSqlSelections( selectClause );
}
@OverrideView on GitHub (pinned to fad1729dce)
Solutions
- Remove the lock from the union query and lock the selected ids in a separate simple query
- Rewrite the set operation as a single query spec or a join so no QueryGroup is present
- Allow follow-on locking (drop the DISALLOW strategy/hint) so Hibernate falls back to locking after fetch
- Run the locked read as a native query that locks a derived table TimesTen accepts
Example fix
// before
TypedQuery<Long> q = em.createQuery(
"select a.id from A a union select b.id from B b", Long.class);
q.setLockMode(LockModeType.PESSIMISTIC_WRITE);
q.setHint("hibernate.query.followOnLocking", false); // -> throws
// after: fetch ids unlocked, then lock them
List<Long> ids = em.createQuery(
"select a.id from A a union select b.id from B b", Long.class).getResultList();
List<A> locked = em.createQuery("select a from A a where a.id in :ids", A.class)
.setParameter("ids", ids)
.setLockMode(LockModeType.PESSIMISTIC_WRITE)
.getResultList(); Defensive patterns
Strategy: try-catch
Validate before calling
// Before locking on TimesTen, verify the query has no set operators
static boolean safeToLock(String hql, Dialect d) {
if (!(d instanceof TimesTenDialect)) return true;
String u = hql.toLowerCase(Locale.ROOT);
return !(u.contains(" union ") || u.contains(" intersect ") || u.contains(" except "));
} Try / catch
try {
return q.getResultList(); // q has PESSIMISTIC_WRITE + followOnLocking=false
} catch (IllegalQueryOperationException e) {
if (e.getMessage() != null && e.getMessage().contains("set operators")) {
List<Long> ids = unlockedUnionQuery(); // 1. fetch ids without lock
return lockByIds(ids); // 2. lock via simple IN query
}
throw e;
} Prevention
- Never apply pessimistic locks generically to reporting/union queries
- On TimesTen, lock by primary key in a follow-up simple query instead of the union itself
- Do not set hibernate.query.followOnLocking=false unless you know the query shape it protects
When it happens
Trigger: A query containing UNION/INTERSECT/EXCEPT combined with a pessimistic lock (LockModeType.PESSIMISTIC_WRITE, setLockMode, HQL 'for update') while follow-on locking is disallowed — e.g. query.setFollowOnStrategy(Locking.FollowOn.DISALLOW) or the hint hibernate.query.followOnLocking=false, commonly set when locking plus pagination is used.
Common situations: Generic repository layers that apply pessimistic locking to every lookup query; reporting queries built from unions that later gain a lock; lock + setMaxResults flows where follow-on locking was deliberately disabled.
Related errors
- Connection lock-timeout does not accept skip-locked
- Locking with joins is not supported
- Locking with set operators is not supported
- SingleStore doesn't support UNION/UNION ALL with limit claus
- Summarization is not supported by DBMS!
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/a0a339f9627e2ac6.
Report an issue: GitHub.