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

  1. Enable follow-on locking on this query via LockOptions.setFollowOnLocking(true).
  2. Remove the lock from aggregate queries — aggregate results are not row locks; lock the contributing rows in a separate FOR UPDATE select if needed.
  3. 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

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


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