hibernate/hibernate-orm · error · IllegalStateException

JPA selection is not compound

Error message

JPA selection is not compound

What it means

getCompoundSelectionItems() is only meaningful for compound (multi-item) selections such as multiselect/array/tuple constructions. SqmSubQuery.isCompoundSelection() returns false — a subquery is always a single scalar expression — so calling getCompoundSelectionItems() on a subquery throws IllegalStateException. Code that processes arbitrary Selection trees must branch on isCompoundSelection() first.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/select/SqmSubQuery.java:336

		return (SqmExpression<T>) super.getSelection();
	}

	@Override
	public boolean isCompoundSelection() {
		// A subquery is always a single/scalar expression, so it can't be a compound selection
		return false;
	}

	@Override
	public List<? extends JpaSelection<?>> getSelectionItems() {
		return Collections.emptyList();
	}

	@Nonnull
	@Override
	public List<Selection<?>> getCompoundSelectionItems() {
		// A subquery is always a single/scalar expression, so it can't be a compound selection
		throw new IllegalStateException( "JPA selection is not compound" );
	}

	@Nonnull
	@Override
	public SqmSubQuery<T> distinct(boolean distinct) {
		super.distinct( distinct );
		return this;
	}

	@Nonnull
	@Override
	public SqmSubQuery<T> where(@Nonnull Expression<Boolean> restriction) {
		super.where( restriction );
		return this;
	}

	@Nonnull
	@Override

View on GitHub (pinned to fad1729dce)

Solutions

  1. Check selection.isCompoundSelection() before calling getCompoundSelectionItems() and treat false as 'single expression'
  2. For subqueries, use getSelectionItems() (which returns an empty list for SqmSubQuery) or simply handle the subquery as one atomic item
  3. Reorder the mapper to switch on the concrete JpaSelection subtype instead of assuming compound

Example fix

// before
for (Selection<?> s : query.getSelectionList()) {
    ((CompoundSelection<?>) s).getCompoundSelectionItems(); // IllegalStateException when s is a subquery
}

// after
for (Selection<?> s : query.getSelectionList()) {
    if (s.isCompoundSelection()) {
        ((CompoundSelection<?>) s).getCompoundSelectionItems();
    } else {
        handleSingleExpression(s); // subquery lands here
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (selection.isCompoundSelection()) { items = selection.getCompoundSelectionItems(); } else { /* single expression, possibly a subquery */ }

Type guard

static boolean isCompound(Selection<?> s) { return s.isCompoundSelection(); } // false for SqmSubQuery

Try / catch

try { items = sel.getCompoundSelectionItems(); } catch (IllegalStateException e) { if (e.getMessage().contains("not compound")) items = List.of(sel); else throw e; }

Prevention

When it happens

Trigger: Generic result-mapping utilities, tuple transformers, or criteria walkers that unconditionally call getCompoundSelectionItems() (or cast Selection to CompoundSelection) on every node, encountering a Selection that is actually a Subquery.

Common situations: In-house criteria-to-SQL/DTO mappers; libraries that emulate Spring Data projections over JPA criteria; code migrated from a query type that was compound to one where a subquery now appears in the selection list.

Related errors


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