{"record":{"id":"f9e81d6e196fa57b","repo":"hibernate/hibernate-orm","slug":"cannot-call-getparent-on-update-delete-criteria","errorCode":null,"errorMessage":"Cannot call getParent() on update/delete criteria","messagePattern":"Cannot call getParent\\(\\) on update/delete criteria","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/select/SqmSubQuery.java","lineNumber":250,"sourceCode":"\t\t\t);\n\t\t}\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic SqmQuery<?> getContainingQuery() {\n\t\treturn parent;\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic SqmSelectQuery<?> getParent() {\n\t\t// JPA only allows subqueries on select queries\n\t\tif ( getContainingQuery() instanceof SqmSelectQuery<?> sqmSelectQuery ) {\n\t\t\treturn sqmSelectQuery;\n\t\t}\n\t\telse {\n\t\t\tthrow new IllegalStateException( \"Cannot call getParent() on update/delete criteria\" );\n\t\t}\n\t}\n\n\t@Override\n\tpublic @Nullable String getAlias() {\n\t\treturn alias;\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic SqmSubQuery<T> alias(@Nonnull String alias) {\n\t\tthis.alias = alias;\n\t\treturn this;\n\t}\n\n\t@Nonnull\n\t@Override\n\tpublic SqmSubQuery<T> select(@Nonnull Expression<T> expression) {","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/spi/select/SqmSubQuery.java#L232-L268","documentation":"JPA allows subqueries only inside select queries, so SubQuery.getParent() is declared to return the enclosing select AbstractQuery. Hibernate's SQM model additionally permits subqueries in the WHERE clause of UPDATE and DELETE statements, and for those subqueries there is no select parent. getParent() therefore throws IllegalStateException; the owning statement of any kind is available via getContainingQuery().","triggerScenarios":"Creating a subquery from a CriteriaUpdate/CriteriaDelete (update.subquery(...)) for a bulk-statement predicate and then calling subquery.getParent() on it — typically from generic tree-walking or query-rewriting code written against the plain JPA API.","commonSituations":"Shared criteria traversal utilities (query transformers, audit loggers, count-query generators) that call getParent() to walk up the tree and are later applied to mass-update/delete statements with correlated subqueries.","solutions":["Replace getParent() with Hibernate's getContainingQuery(), which returns the SqmQuery<?> owner regardless of statement type","Guard the call: only invoke getParent() when subquery.getContainingQuery() instanceof JpaSelectQuery / SqmSelectQuery","Restructure the predicate so the subquery lives in a select query (e.g., build the values in a select query first)"],"exampleFix":"// before\nSubquery<Long> sq = update.subquery(Long.class);\n...\nAbstractQuery<?> parent = sq.getParent(); // IllegalStateException on update/delete\n\n// after\nSqmSubQuery<Long> sq = (SqmSubQuery<Long>) update.subquery(Long.class);\n...\nSqmQuery<?> owner = sq.getContainingQuery(); // works for select, update and delete","handlingStrategy":"type-guard","validationCode":"if (sq.getContainingQuery() instanceof JpaSelectQuery<?>) { AbstractQuery<?> p = sq.getParent(); } else { /* update/delete: no select parent */ }","typeGuard":"static Optional<JpaSelectQuery<?>> selectParentOf(SqmSubQuery<?> sq) {\n    return sq.getContainingQuery() instanceof JpaSelectQuery<?> sel\n            ? Optional.of(sel)\n            : Optional.empty();\n}","tryCatchPattern":"try { return sq.getParent(); } catch (IllegalStateException e) { if (e.getMessage().contains(\"update/delete\")) return sq.getContainingQuery(); throw e; }","preventionTips":["Prefer getContainingQuery() over getParent() in traversal utilities that may see update/delete statements","Document which of your helper methods assume select-only subqueries","Add a test that runs your criteria walker against a CriteriaUpdate with a subquery"],"tags":["hibernate","criteria-api","subquery","bulk-update","bulk-delete"],"backgroundTag":"subquery-parent-access","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}