hibernate/hibernate-orm · error · IllegalSelectQueryException

Expecting a SELECT Query [%s], but found %s

Error message

Expecting a SELECT Query [%s], but found %s

What it means

SqmUtil.asSelectStatement is the choke point where Hibernate asserts that an SQM statement it is about to treat as a query (typed results, scrolling, paging) really is an SqmSelectStatement. Passing an update/delete/insert statement — usually because HQL text like 'update ...' was handed to a select-oriented API — throws IllegalSelectQueryException with both class names in the message.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java:147

	public static boolean isSelect(SqmStatement<?> sqm) {
		return sqm instanceof SqmSelectStatement;
	}

	public static boolean isMutation(SqmStatement<?> sqm) {
		return sqm instanceof SqmDmlStatement;
	}

	public static <T> boolean isRestrictedMutation(SqmStatement<T> sqmStatement) {
		return sqmStatement instanceof SqmDeleteOrUpdateStatement;
	}

	public static <R> SqmSelectStatement<R> asSelectStatement(SqmStatement<?> sqm, String hqlString) {
		if ( sqm instanceof SqmSelectStatement<?> selectAst ) {
			//noinspection unchecked
			return (SqmSelectStatement<R>) selectAst;
		}
		else {
			throw new IllegalSelectQueryException(
					String.format(
							Locale.ROOT,
							"Expecting a SELECT Query [%s], but found %s",
							SqmSelectStatement.class.getName(),
							sqm.getClass().getName()
					),
					hqlString
			);
		}
	}

	public static void verifyIsSelectStatement(SqmStatement<?> sqm, String hqlString) {
		if ( ! isSelect( sqm ) ) {
			throw new IllegalSelectQueryException(
					String.format(
							Locale.ROOT,
							"Expecting a SELECT Query [%s], but found %s",
							SqmSelectStatement.class.getName(),

View on GitHub (pinned to fad1729dce)

Solutions

  1. Use the mutation API for non-select HQL: em.createMutationQuery(hql).executeUpdate() (Hibernate 6+) / session.createMutationQuery(...).
  2. If the statement should be a select, fix the HQL itself (missing 'select'/'from' clause, wrong keyword, truncated string).
  3. Branch on the first keyword of dynamic HQL before choosing createQuery vs createMutationQuery.

Example fix

// before
Query<Order> q = em.createQuery("update Order o set o.status = :s", Order.class);
q.getResultList(); // Expecting a SELECT Query [...], but found SqmUpdateStatement

// after
MutationQuery q = em.createMutationQuery("update Order o set o.status = :s").setParameter("s", "OPEN");
q.executeUpdate();
Defensive patterns

Strategy: validation

Validate before calling

static boolean isSelectHql(String hql) {
    String head = hql.trim().toLowerCase(Locale.ROOT);
    return head.startsWith("select") || head.startsWith("from");
}
if (!isSelectHql(hql)) throw new IllegalArgumentException("use createMutationQuery for: " + hql);

Type guard

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

Try / catch

try {
    results = em.createQuery(hql, type).getResultList();
} catch (IllegalSelectQueryException e) {
    // HQL was update/delete/insert: route to the mutation API instead
    em.createMutationQuery(hql).executeUpdate();
}

Prevention

When it happens

Trigger: em.createQuery(hql, Order.class) where hql = 'update Order o set ...' (typed query creation implies select); session.createQuery(hql).getSingleResult() / setMaxResults / scroll on mutation HQL; feeding a mutation query through APIs like SQM copying or name-based typed lookups that internally call asSelectStatement.

Common situations: Concatenated/templated HQL that flips between select and bulk statements; DML statements routed to createQuery instead of createMutationQuery during refactors; migration from Hibernate 5 where some leniency existed; GUI query builders executing user-entered HQL.

Related errors


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