hibernate/hibernate-orm · error · StrictJpaComplianceViolation
ALIASED_FETCH_JOIN
ALIASED_FETCH_JOIN
Error message
Encountered aliased fetch join, but strict JPQL compliance was requested
What it means
After an attribute join is created, consumeJoin checks strict JPQL query compliance: JPQL does not allow assigning an alias to a fetch join, so 'join fetch e.items i' with an explicit alias under 'hibernate.jpa.compliance.query=true' throws StrictJpaComplianceViolation.Type.ALIASED_FETCH_JOIN.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java:2302
final boolean fetch = parserJoin.FETCH() != null;
if ( fetch && processingStateStack.depth() > 1 ) {
throw new SemanticException( "The 'from' clause of a subquery has a 'fetch'", query );
}
final var joinRestrictionContext = parserJoin.joinRestriction();
// 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();
}View on GitHub (pinned to fad1729dce)
Solutions
- Remove the alias from the fetch join: 'join fetch e.tasks'
- If you need the alias for filtering, split into a fetch join plus a plain join, or drop fetch and use a plain join
- Alternatively disable 'hibernate.jpa.compliance.query'
Example fix
// before select e from Employee e join fetch e.tasks t // after select e from Employee e join fetch e.tasks
Defensive patterns
Strategy: fallback
Validate before calling
static boolean strictQueryCompliance(EntityManagerFactory emf) {
Object v = emf.getProperties().get("hibernate.jpa.compliance.query");
if (v == null) v = emf.getProperties().get("hibernate.jpa.compliance");
return v != null && Boolean.parseBoolean(v.toString());
}
static boolean aliasedFetchJoin(String hql) {
return java.util.regex.Pattern.compile("join\\s+fetch\\s+[\\w.]+\\s+([a-zA-Z_][\\w]*)", java.util.regex.Pattern.CASE_INSENSITIVE)
.matcher(hql).find();
}
// strip aliases from fetch joins before running under strict compliance Type guard
static boolean isAliasedFetchJoinViolation(Throwable t) {
return t instanceof org.hibernate.query.sqm.StrictJpaComplianceViolation
&& ((org.hibernate.query.sqm.StrictJpaComplianceViolation) t).getType() == org.hibernate.query.sqm.StrictJpaComplianceViolation.Type.ALIASED_FETCH_JOIN;
} Try / catch
try {
return em.createQuery(hql, Employee.class).getResultList(); // 'join fetch e.tasks t'
} catch (org.hibernate.query.sqm.StrictJpaComplianceViolation e) {
return em.createQuery(stripFetchAliases(hql), Employee.class).getResultList(); // 'join fetch e.tasks'
} Prevention
- Do not alias fetch joins if the codebase must run with JPA compliance on
- If you need the alias for a condition, use a separate plain join alongside the fetch join
- Encode the compliance policy once in build properties, not per-query
When it happens
Trigger: 'select e from Employee e join fetch e.tasks t' executed with hibernate.jpa.compliance.query=true (alias 't' on a fetch join).
Common situations: Enabling JPA compliance on legacy code that used Hibernate's long-standing aliased-fetch extension; tools generating aliases for every join automatically.
Related errors
- NON_ENTITY_NAME
- UNMAPPED_POLYMORPHISM
- FROM_SUBQUERY
- FROM_FUNCTION
- The 'from' clause of a subquery has a 'fetch'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/53cb8e8a7dc0f566.
Report an issue: GitHub.