hibernate/hibernate-orm · error · IllegalMutationQueryException
Query string is not a mutation
Error message
Query string is not a mutation
What it means
createMutationQuery(String hql) (and named-query paths returning MutationQuery) require an INSERT/UPDATE/DELETE statement. After interpreting the HQL, buildHqlMutationQuery checks that the SQM tree is an SqmDmlStatement; a SELECT produces IllegalMutationQueryException('Query string is not a mutation'). The typed MutationQuery API exists so executeUpdate() semantics are guaranteed at creation time.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java:1765
}
selectionQuery.setComment( hql );
applyQuerySettingsAndHints( selectionQuery );
return selectionQuery;
}
@Override
public MutationQuery createMutationQuery(String hql) {
checksBeforeQueryCreation();
return buildHqlMutationQuery( hql, interpretHql( hql ) );
}
private <T> MutationQueryImplementor<T> buildHqlMutationQuery(String hql, HqlInterpretation<T> interpretation) {
if ( interpretation.getSqmStatement() instanceof SqmDmlStatement<T> mutationAst ) {
return buildHqlMutationQuery( hql, interpretation, mutationAst.getTarget().getJavaType() );
}
else {
throw new IllegalMutationQueryException( "Query string is not a mutation", hql );
}
}
private <T> MutationQueryImplementor<T> buildHqlMutationQuery(String hql, HqlInterpretation<T> interpretation, Class<T> targetType) {
final var mutationQuery = new MutationQueryImpl<>( hql, interpretation, targetType, this );
mutationQuery.setComment( hql );
applyQuerySettingsAndHints( mutationQuery );
return mutationQuery;
}
@Override
@Nonnull
public MutationQuery createStatement(@Nonnull String hqlString) {
// JPA form
try {
return createMutationQuery( hqlString );
}
catch (IllegalMutationQueryException e) {View on GitHub (pinned to fad1729dce)
Solutions
- Use createSelectionQuery/createQuery for SELECT statements; createMutationQuery only for INSERT/UPDATE/DELETE.
- If the string is config- or user-supplied, route by the first HQL keyword (see typeGuard) before choosing the factory method.
- Fix the statement itself when a select was unintentional (e.g., truncated HQL).
Example fix
// before
MutationQuery q = session.createMutationQuery("select p from Person p"); // throws
// after
SelectionQuery<Person> q = session.createSelectionQuery("select p from Person p", Person.class); Defensive patterns
Strategy: type-guard
Type guard
static boolean isMutationHql(String hql) {
String head = hql.stripLeading().toLowerCase(Locale.ROOT);
return head.startsWith("update ") || head.startsWith("delete ") || head.startsWith("insert ");
}
// usage: route dynamic HQL to the right API
if (isMutationHql(hql)) {
session.createMutationQuery(hql).executeUpdate();
} else {
session.createSelectionQuery(hql).list();
} Try / catch
try {
return session.createMutationQuery(hql).executeUpdate();
} catch (IllegalMutationQueryException e) {
throw new IllegalArgumentException("Expected DML but got: " + hql, e); // surface config error, do not auto-rerun
} Prevention
- Never funnel arbitrary HQL strings through createMutationQuery
- Split query routing in generic DAO helpers by statement kind up front
- Use createSelectionQuery for reads even though createQuery also accepts them
When it happens
Trigger: session.createMutationQuery("select p from Person p"); createMutationQuery on a string whose first token is SELECT; routing every query string through createMutationQuery in a generic helper; createNamedQuery variants that must be mutations but resolve to a SELECT.
Common situations: Refactoring createQuery(hql).executeUpdate() to the createMutationQuery API for a select statement; generic DAO/query-router funnels; upgrading to Hibernate 6.3+/7 where the mutation/selection split is enforced strictly.
Related errors
- Expecting a selection query, but found '{}'
- Expecting a restricted mutation query [%s], but found %s
- Could not resolve attribute '%s' of '%s' due to the attribut
- Expecting a SELECT Query [%s], but found %s
- Select item was of wrong entity type
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/2ce213aa46f9283b.
Report an issue: GitHub.