{"record":{"id":"dcec4035b35f0dc2","repo":"hibernate/hibernate-orm","slug":"fetch-join-has-a-with-clause-use-a-filter-inste","errorCode":null,"errorMessage":"Fetch join has a 'with' clause (use a filter instead)","messagePattern":"Fetch join has a 'with' clause \\(use a filter instead\\)","errorType":"exception","errorClass":"SemanticException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java","lineNumber":2309,"sourceCode":"\t\t// Joins are allowed to be reused if they don't have a join condition\n\t\tfinal var allowReuse = joinRestrictionContext == null;\n\t\tdotIdentifierConsumerStack.push( new QualifiedJoinPathConsumer( sqmRoot, joinType, fetch, alias, allowReuse, this ) );\n\t\ttry {\n\t\t\tfinal var join = getJoin( sqmRoot, joinType, qualifiedJoinTargetContext, alias, fetch );\n\t\t\tif ( join instanceof SqmEntityJoin<?,?> || join instanceof SqmDerivedJoin<?> || join instanceof SqmCteJoin<?> ) {\n\t\t\t\tsqmRoot.addSqmJoin( join );\n\t\t\t}\n\t\t\telse if ( join instanceof SqmAttributeJoin<?, ?> attributeJoin ) {\n\t\t\t\tif ( getCreationOptions().useStrictJpaCompliance() ) {\n\t\t\t\t\tif ( join.getExplicitAlias() != null && attributeJoin.isFetched() ) {\n\t\t\t\t\t\tthrow new StrictJpaComplianceViolation(\n\t\t\t\t\t\t\t\t\"Encountered aliased fetch join, but strict JPQL compliance was requested\",\n\t\t\t\t\t\t\t\tStrictJpaComplianceViolation.Type.ALIASED_FETCH_JOIN\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ( joinRestrictionContext != null && attributeJoin.isFetched() ) {\n\t\t\t\t\tthrow new SemanticException( \"Fetch join has a 'with' clause (use a filter instead)\", query );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( joinRestrictionContext != null ) {\n\t\t\t\tdotIdentifierConsumerStack.push( new QualifiedJoinPredicatePathConsumer( join, this ) );\n\t\t\t\ttry {\n\t\t\t\t\tjoin.setJoinPredicate( (SqmPredicate) joinRestrictionContext.getChild( 1 ).accept( this ) );\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tdotIdentifierConsumerStack.pop();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\tdotIdentifierConsumerStack.pop();\n\t\t}\n\t}\n","sourceCodeStart":2291,"sourceCodeEnd":2327,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java#L2291-L2327","documentation":"consumeJoin rejects a fetch join that declares a 'with' restriction ('join fetch e.tasks t with t.done = true'). A conditional fetch join would build partially initialized collections and break fetch semantics, so Hibernate throws SemanticException and points to filters as the supported way to restrict fetched associations.","triggerScenarios":"'select e from Employee e join fetch e.tasks t with t.active = true'; any 'join fetch ... with <predicate>' in HQL.","commonSituations":"Trying to load only part of a collection eagerly (the classic XY problem); porting native SQL left joins with conditions into fetch joins; upgrading Hibernate where older versions tolerated some forms.","solutions":["Use a plain (non-fetch) join with the condition if you only need filtered tuples: 'join e.tasks t where t.active = true'","Apply @FilterDef/@Filter on the association so fetched rows are restricted consistently, then fetch without 'with'","Model the restricted set as a separate association or query it separately to keep fetch semantics intact"],"exampleFix":"// before\nselect e from Employee e join fetch e.tasks t with t.active = true\n\n// after (filtered tuples, no eager collection init)\nselect e from Employee e join e.tasks t where t.active = true\n\n// or restrict the association via @Filter and fetch plainly\nselect e from Employee e join fetch e.tasks","handlingStrategy":"validation","validationCode":"// Reject 'join fetch ... with ...' before execution\nstatic boolean conditionalFetchJoin(String hql) {\n    return java.util.regex.Pattern.compile(\"join\\\\s+fetch\\\\s+[^;]+?\\\\s+with\\\\s\", java.util.regex.Pattern.CASE_INSENSITIVE | java.util.regex.Pattern.DOTALL).matcher(hql).find();\n}\nif (conditionalFetchJoin(hql)) throw new IllegalArgumentException(\"Fetch joins cannot have 'with' conditions - use filters or a plain join\");","typeGuard":null,"tryCatchPattern":"try {\n    return em.createQuery(hql, Employee.class).getResultList();\n} catch (org.hibernate.query.sqm.SemanticException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Fetch join has a 'with' clause\")) {\n        throw new IllegalArgumentException(\"Split into a plain join for filtering, or use @Filter on the association\", e);\n    }\n    throw e;\n}","preventionTips":["Never put row conditions on fetch joins; conditionally fetched collections end up partially initialized","Model association-level restrictions with @FilterDef/@Filter so they apply wherever the collection is fetched","If filtered tuples (not filtered collections) are the goal, use a plain join plus where"],"tags":["hql","fetch-join","with-clause","hibernate","filter"],"backgroundTag":"fetch-join-with-condition","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}