hibernate/hibernate-orm · error · SemanticException
Fetch join has a 'with' clause (use a filter instead)
Error message
Fetch join has a 'with' clause (use a filter instead)
What it means
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.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java:2309
// Joins are allowed to be reused if they don't have a join condition
final var allowReuse = joinRestrictionContext == null;
dotIdentifierConsumerStack.push( new QualifiedJoinPathConsumer( sqmRoot, joinType, fetch, alias, allowReuse, this ) );
try {
final var join = getJoin( sqmRoot, joinType, qualifiedJoinTargetContext, alias, fetch );
if ( join instanceof SqmEntityJoin<?,?> || join instanceof SqmDerivedJoin<?> || join instanceof SqmCteJoin<?> ) {
sqmRoot.addSqmJoin( join );
}
else if ( join instanceof SqmAttributeJoin<?, ?> attributeJoin ) {
if ( getCreationOptions().useStrictJpaCompliance() ) {
if ( join.getExplicitAlias() != null && attributeJoin.isFetched() ) {
throw new StrictJpaComplianceViolation(
"Encountered aliased fetch join, but strict JPQL compliance was requested",
StrictJpaComplianceViolation.Type.ALIASED_FETCH_JOIN
);
}
}
if ( joinRestrictionContext != null && attributeJoin.isFetched() ) {
throw new SemanticException( "Fetch join has a 'with' clause (use a filter instead)", query );
}
}
if ( joinRestrictionContext != null ) {
dotIdentifierConsumerStack.push( new QualifiedJoinPredicatePathConsumer( join, this ) );
try {
join.setJoinPredicate( (SqmPredicate) joinRestrictionContext.getChild( 1 ).accept( this ) );
}
finally {
dotIdentifierConsumerStack.pop();
}
}
}
finally {
dotIdentifierConsumerStack.pop();
}
}
View on GitHub (pinned to fad1729dce)
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
Example fix
// before select e from Employee e join fetch e.tasks t with t.active = true // after (filtered tuples, no eager collection init) select e from Employee e join e.tasks t where t.active = true // or restrict the association via @Filter and fetch plainly select e from Employee e join fetch e.tasks
Defensive patterns
Strategy: validation
Validate before calling
// Reject 'join fetch ... with ...' before execution
static boolean conditionalFetchJoin(String hql) {
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();
}
if (conditionalFetchJoin(hql)) throw new IllegalArgumentException("Fetch joins cannot have 'with' conditions - use filters or a plain join"); Try / catch
try {
return em.createQuery(hql, Employee.class).getResultList();
} catch (org.hibernate.query.sqm.SemanticException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Fetch join has a 'with' clause")) {
throw new IllegalArgumentException("Split into a plain join for filtering, or use @Filter on the association", e);
}
throw e;
} Prevention
- 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
When it happens
Trigger: 'select e from Employee e join fetch e.tasks t with t.active = true'; any 'join fetch ... with <predicate>' in HQL.
Common situations: 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.
Related errors
- The 'from' clause of a subquery has a 'fetch'
- ALIASED_FETCH_JOIN
- The 'from' clause of a subquery has a 'fetch' join
- Insert conflict 'do update' clause with constraint name is n
- field type not supported on Derby: " + unit
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/dcec4035b35f0dc2.
Report an issue: GitHub.