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

  1. Remove the alias from the fetch join: 'join fetch e.tasks'
  2. If you need the alias for filtering, split into a fetch join plus a plain join, or drop fetch and use a plain join
  3. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/53cb8e8a7dc0f566. Report an issue: GitHub.