{"record":{"id":"33f81e9f96bd5048","repo":"hibernate/hibernate-orm","slug":"delete-query-cannot-be-sub-query","errorCode":null,"errorMessage":"DELETE query cannot be sub-query","messagePattern":"DELETE query cannot be sub-query","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/delete/SqmDeleteStatement.java","lineNumber":152,"sourceCode":"\t\treturn walker.visitDeleteStatement( this );\n\t}\n\n\t@Override\n\tpublic void appendHqlString(StringBuilder hql, SqmRenderContext context) {\n\t\tappendHqlCteString( hql, context );\n\t\thql.append( \"delete from \" );\n\t\tfinal SqmRoot<T> root = getTarget();\n\t\thql.append( root.getEntityName() );\n\t\thql.append( ' ' ).append( root.resolveAlias( context ) );\n\t\tSqmFromClause.appendJoins( root, hql, context );\n\t\tSqmFromClause.appendTreatJoins( root, hql, context );\n\t\tsuper.appendHqlString( hql, context );\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic <U> Subquery<U> subquery(@Nonnull EntityType<U> type) {\n\t\tthrow new UnsupportedOperationException( \"DELETE query cannot be sub-query\" );\n\t}\n}\n","sourceCodeStart":134,"sourceCodeEnd":155,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/delete/SqmDeleteStatement.java#L134-L155","documentation":"SqmDeleteStatement overrides AbstractQuery.subquery(EntityType) to throw UnsupportedOperationException: in the criteria model a DELETE statement cannot host subqueries. Criteria delete statements only support a where predicate on the target root; subselects must be expressed differently (e.g. HQL mutation query or a two-step select-then-delete).","triggerScenarios":"Subquery<Long> sq = cb.createCriteriaDelete(Order.class).subquery(Customer.class); directly, or generic code that walks any AbstractQuery/AbstractSelectionQuery and uniformly calls subquery(type) on it.","commonSituations":"Porting 'delete where x in (select ...)' logic written for SQL or for a select criteria query; reusable restriction builders that add exists()/in() predicates via query.subquery(...); framework code that processes criteria trees of any kind.","solutions":["Use an HQL mutation query with a subselect: em.createMutationQuery(\"delete from Order o where o.customer.id in (select c.id from Customer c where c.status = :s)\").","Run a select criteria query first to collect the affected IDs, then issue the delete restricted to those IDs.","Guard generic code with a query-type check (only SqmSelectQuery supports subquery())."],"exampleFix":"// before\nCriteriaDelete<Order> delete = cb.createCriteriaDelete( Order.class );\nSubquery<Long> sq = delete.subquery( Long.class ); // throws UnsupportedOperationException\n// after\nem.createMutationQuery(\n    \"delete from Order o where o.customer.id in (select c.id from Customer c where c.status = :st)\"\n).setParameter( \"st\", \"INACTIVE\" ).executeUpdate();","handlingStrategy":"type-guard","validationCode":"// guard generic tree-processing before calling subquery():\nif ( query instanceof SqmSelectQuery<?> ) {\n    Subquery<U> sq = ( (AbstractQuery<?>) query ).subquery( type );\n} else {\n    // DML statement: use HQL mutation query or two-step select+delete instead\n}","typeGuard":"static boolean canHostSubquery(jakarta.persistence.criteria.AbstractQuery<?> q) {\n    return q instanceof SqmSelectQuery; // delete (and other DML) statements reject subquery()\n}","tryCatchPattern":"try {\n    sub = deleteStatement.subquery( type );\n} catch (UnsupportedOperationException e) {\n    // \"DELETE query cannot be sub-query\" -> fall back to HQL mutation query with subselect\n    em.createMutationQuery( hqlDeleteWithSubselect ).executeUpdate();\n}","preventionTips":["Never call AbstractQuery.subquery(...) without checking the concrete query kind.","Prefer HQL mutation queries for 'delete where ... in (select ...)' semantics.","In reusable predicate builders, accept a Subquery/AbstractQuery parameter supplied by select-query callers instead of creating it from the passed query."],"tags":["hibernate","criteria-api","dml","delete","subquery","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}