hibernate/hibernate-orm · error · IllegalQueryOperationException
Locking with aggregate functions is not supported
Error message
Locking with aggregate functions is not supported
What it means
Last branch of determineLockingStrategy: if the query spec has aggregate functions (count, sum, avg, ...), a native locking clause is impossible, so follow-on locking is required; with FollowOn.DISALLOW the translation throws IllegalQueryOperationException("Locking with aggregate functions is not supported"). Note this triggers on aggregates anywhere in the query spec, even without GROUP BY.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java:1976
}
if ( !dialect.supportsOuterJoinForUpdate() ) {
if ( lockingClauseStrategy != null && lockingClauseStrategy.containsOuterJoins() ) {
// we have any outer joins to lock, but the dialect does not support locking outer joins
// -we need to use follow-on locking if allowed
if ( followOnStrategy == Locking.FollowOn.DISALLOW ) {
throw new IllegalQueryOperationException( "Locking with OUTER joins is not supported" );
}
else if ( followOnStrategy == Locking.FollowOn.IGNORE ) {
return LockStrategy.NONE;
}
strategy = LockStrategy.FOLLOW_ON;
}
}
if ( hasAggregateFunctions( querySpec ) ) {
if ( followOnStrategy == Locking.FollowOn.DISALLOW ) {
throw new IllegalQueryOperationException( "Locking with aggregate functions is not supported" );
}
else if ( followOnStrategy == Locking.FollowOn.IGNORE ) {
return LockStrategy.NONE;
}
strategy = LockStrategy.FOLLOW_ON;
}
return strategy;
}
protected void visitConflictClause(ConflictClause conflictClause) {
if ( conflictClause != null ) {
// By default, we only support do nothing with an optional constraint name
if ( !conflictClause.getConstraintColumnNames().isEmpty() ) {
throw new IllegalQueryOperationException( "Insert conflict clause with constraint column names is not supported" );
}
if ( conflictClause.isDoUpdate() ) {
throw new IllegalQueryOperationException( "Insert conflict do update clause is not supported" );View on GitHub (pinned to fad1729dce)
Solutions
- Enable follow-on locking on this query via LockOptions.setFollowOnLocking(true).
- Remove the lock from aggregate queries — aggregate results are not row locks; lock the contributing rows in a separate FOR UPDATE select if needed.
- Split the query: lock ids in a plain select, compute aggregates unlocked.
Example fix
// before — locking an aggregate query
Long c = em.createQuery("select count(e) from Employee e where e.region = :r", Long.class)
.setParameter("r", "EMEA")
.setLockMode(LockModeType.PESSIMISTIC_WRITE).getSingleResult();
// after — lock rows, not aggregates
List<Long> ids = em.createQuery("select e.id from Employee e where e.region = :r", Long.class)
.setParameter("r", "EMEA")
.setLockMode(LockModeType.PESSIMISTIC_WRITE).getResultList();
Long c = (long) ids.size(); Defensive patterns
Strategy: validation
Validate before calling
if (hql.matches("(?i).*\\b(count|sum|avg|min|max)\\s*\\(.*") && lockMode != null && lockMode.isPessimistic()) {
throw new IllegalArgumentException("Aggregate queries cannot take a native locking clause; lock rows separately");
} Try / catch
try { em.createQuery(hql).setLockMode(LockModeType.PESSIMISTIC_WRITE).getSingleResult(); }
catch (org.hibernate.query.IllegalQueryOperationException e) {
if (e.getMessage().equals("Locking with aggregate functions is not supported")) {
// rerun without lock, then lock contributing rows by id
} else { throw e; }
} Prevention
- Keep aggregate queries unlocked; lock the underlying rows by id when needed.
- Do not apply repository-level @Lock defaults to all finder methods.
When it happens
Trigger: Pessimistic lock mode with follow-on disallowed on a query containing aggregate functions — e.g., 'select count(e) from Employee e' or 'select e, sum(o.total) from ...' with setLockMode(PESSIMISTIC_WRITE).
Common situations: Existence/count checks wrapped in locks; DTO projections mixing entities and aggregates under @Lock; generic service layers applying one lock mode to every query including aggregates.
Related errors
- Locking with GROUP BY is not supported
- Locking with HAVING is not supported
- Locking with DISTINCT is not supported
- Duplicate named query '%s'
- Duplicate named stored procedure '{}'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/e9278101265485f8.
Report an issue: GitHub.