hibernate/hibernate-orm · error · IllegalSelectQueryException

Expecting a selection query, but found '{}'

Error message

Expecting a selection query, but found '{}'

What it means

Selection-query APIs (createSelectionQuery and typed/named variants) pass through checkSelectionQuery(): the interpreted SQM statement must be an SqmSelectStatement. An UPDATE/DELETE/INSERT string yields IllegalSelectQueryException('Expecting a selection query, but found <hql>'). Like error 1553, this guards the selection/mutation API split so result-list semantics are guaranteed.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java:2075

		return getMappingMetamodel().getEntityDescriptor( entityName );
	}

	// Hibernate Reactive may need to use this
	protected final CollectionPersister requireCollectionPersister(String roleName) {
		return getMappingMetamodel().getCollectionDescriptor( roleName );
	}

	protected HqlInterpretation<?> interpretHql(String hql) {
		return interpretHql( hql, null );
	}

	protected <R> HqlInterpretation<R> interpretHql(String hql, Class<R> resultType) {
		return getFactory().getQueryEngine().interpretHql( hql, resultType );
	}

	protected static void checkSelectionQuery(String hql, HqlInterpretation<?> hqlInterpretation) {
		if ( !( hqlInterpretation.getSqmStatement() instanceof SqmSelectStatement ) ) {
			throw new IllegalSelectQueryException( "Expecting a selection query, but found '" + hql + "'", hql);
		}
	}

	protected static <R> void checkResultType(Class<R> expectedResultType, SelectionQuery<R> query) {
		final var resultType = query.getResultType();
		if ( !expectedResultType.isAssignableFrom( resultType ) ) {
			throw new QueryTypeMismatchException(
					String.format(
							Locale.ROOT,
							"Incorrect query result type: query produces '%s' but type '%s' was given",
							expectedResultType.getName(),
							resultType.getName()
					)
			);
		}
	}

	protected NamedResultSetMappingMemento getResultSetMappingMemento(String resultSetMappingName) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Route DML to createMutationQuery/executeUpdate; use createSelectionQuery only for SELECT.
  2. Fix the HQL when a select was intended but the statement parses as DML.
  3. For named queries, match the creation API to the kind declared in @NamedQuery.

Example fix

// before
SelectionQuery<?> q = session.createSelectionQuery("update Account a set a.locked = true"); // throws
// after
MutationQuery q = session.createMutationQuery("update Account a set a.locked = true");
q.executeUpdate();
Defensive patterns

Strategy: type-guard

Type guard

static boolean isSelectHql(String hql) {
    String head = hql.stripLeading().toLowerCase(Locale.ROOT);
    return !(head.startsWith("update ") || head.startsWith("delete ") || head.startsWith("insert "));
}

// usage
if (isSelectHql(hql)) {
    results = session.createSelectionQuery(hql, type).list();
} else {
    session.createMutationQuery(hql).executeUpdate();
}

Try / catch

try {
    return session.createSelectionQuery(hql, type).list();
} catch (IllegalSelectQueryException e) {
    throw new IllegalArgumentException("Expected SELECT but got: " + hql, e);
}

Prevention

When it happens

Trigger: session.createSelectionQuery("update Account a set a.locked = true"); createSelectionQuery(hql, type) with DML; createNamedQuery(name, type) where the named query is defined as an update/delete; generic helpers funneling all strings into selection creation.

Common situations: Shared DAO query methods that build HQL dynamically and pick the wrong factory; porting Hibernate 5 code where Query served both roles; a malformed select that parses as DML (missing 'from' or a stray 'update' keyword).

Related errors


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