{"record":{"id":"2ce213aa46f9283b","repo":"hibernate/hibernate-orm","slug":"query-string-is-not-a-mutation","errorCode":null,"errorMessage":"Query string is not a mutation","messagePattern":"Query string is not a mutation","errorType":"exception","errorClass":"IllegalMutationQueryException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java","lineNumber":1765,"sourceCode":"\t\t}\n\t\tselectionQuery.setComment( hql );\n\t\tapplyQuerySettingsAndHints( selectionQuery );\n\n\t\treturn selectionQuery;\n\t}\n\n\t@Override\n\tpublic MutationQuery createMutationQuery(String hql) {\n\t\tchecksBeforeQueryCreation();\n\t\treturn buildHqlMutationQuery( hql, interpretHql( hql ) );\n\t}\n\n\tprivate <T> MutationQueryImplementor<T> buildHqlMutationQuery(String hql, HqlInterpretation<T> interpretation) {\n\t\tif ( interpretation.getSqmStatement() instanceof SqmDmlStatement<T> mutationAst ) {\n\t\t\treturn buildHqlMutationQuery( hql, interpretation, mutationAst.getTarget().getJavaType() );\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalMutationQueryException( \"Query string is not a mutation\", hql );\n\t\t}\n\t}\n\n\tprivate <T> MutationQueryImplementor<T> buildHqlMutationQuery(String hql, HqlInterpretation<T> interpretation, Class<T> targetType) {\n\t\tfinal var mutationQuery = new MutationQueryImpl<>( hql, interpretation, targetType, this );\n\t\tmutationQuery.setComment( hql );\n\t\tapplyQuerySettingsAndHints( mutationQuery );\n\t\treturn mutationQuery;\n\t}\n\n\t@Override\n\t@Nonnull\n\tpublic MutationQuery createStatement(@Nonnull String hqlString) {\n\t\t// JPA form\n\t\ttry {\n\t\t\treturn createMutationQuery( hqlString );\n\t\t}\n\t\tcatch (IllegalMutationQueryException e) {","sourceCodeStart":1747,"sourceCodeEnd":1783,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java#L1747-L1783","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\nMutationQuery q = session.createMutationQuery(\"select p from Person p\"); // throws\n// after\nSelectionQuery<Person> q = session.createSelectionQuery(\"select p from Person p\", Person.class);","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"static boolean isMutationHql(String hql) {\n    String head = hql.stripLeading().toLowerCase(Locale.ROOT);\n    return head.startsWith(\"update \") || head.startsWith(\"delete \") || head.startsWith(\"insert \");\n}\n\n// usage: route dynamic HQL to the right API\nif (isMutationHql(hql)) {\n    session.createMutationQuery(hql).executeUpdate();\n} else {\n    session.createSelectionQuery(hql).list();\n}","tryCatchPattern":"try {\n    return session.createMutationQuery(hql).executeUpdate();\n} catch (IllegalMutationQueryException e) {\n    throw new IllegalArgumentException(\"Expected DML but got: \" + hql, e); // surface config error, do not auto-rerun\n}","preventionTips":["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"],"tags":["hibernate","hql","mutation-query","query-api","orm"],"backgroundTag":"query-type-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}