{"record":{"id":"53cb8e8a7dc0f566","repo":"hibernate/hibernate-orm","slug":"aliased-fetch-join","errorCode":"ALIASED_FETCH_JOIN","errorMessage":"Encountered aliased fetch join, but strict JPQL compliance was requested","messagePattern":"Encountered aliased fetch join, but strict JPQL compliance was requested","errorType":"exception","errorClass":"StrictJpaComplianceViolation","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java","lineNumber":2302,"sourceCode":"\t\tfinal boolean fetch = parserJoin.FETCH() != null;\n\n\t\tif ( fetch && processingStateStack.depth() > 1 ) {\n\t\t\tthrow new SemanticException( \"The 'from' clause of a subquery has a 'fetch'\", query );\n\t\t}\n\n\t\tfinal var joinRestrictionContext = parserJoin.joinRestriction();\n\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}","sourceCodeStart":2284,"sourceCodeEnd":2320,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java#L2284-L2320","documentation":"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.","triggerScenarios":"'select e from Employee e join fetch e.tasks t' executed with hibernate.jpa.compliance.query=true (alias 't' on a fetch join).","commonSituations":"Enabling JPA compliance on legacy code that used Hibernate's long-standing aliased-fetch extension; tools generating aliases for every join automatically.","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'"],"exampleFix":"// before\nselect e from Employee e join fetch e.tasks t\n\n// after\nselect e from Employee e join fetch e.tasks","handlingStrategy":"fallback","validationCode":"static boolean strictQueryCompliance(EntityManagerFactory emf) {\n    Object v = emf.getProperties().get(\"hibernate.jpa.compliance.query\");\n    if (v == null) v = emf.getProperties().get(\"hibernate.jpa.compliance\");\n    return v != null && Boolean.parseBoolean(v.toString());\n}\n\nstatic boolean aliasedFetchJoin(String hql) {\n    return java.util.regex.Pattern.compile(\"join\\\\s+fetch\\\\s+[\\\\w.]+\\\\s+([a-zA-Z_][\\\\w]*)\", java.util.regex.Pattern.CASE_INSENSITIVE)\n            .matcher(hql).find();\n}\n// strip aliases from fetch joins before running under strict compliance","typeGuard":"static boolean isAliasedFetchJoinViolation(Throwable t) {\n    return t instanceof org.hibernate.query.sqm.StrictJpaComplianceViolation\n            && ((org.hibernate.query.sqm.StrictJpaComplianceViolation) t).getType() == org.hibernate.query.sqm.StrictJpaComplianceViolation.Type.ALIASED_FETCH_JOIN;\n}","tryCatchPattern":"try {\n    return em.createQuery(hql, Employee.class).getResultList();        // 'join fetch e.tasks t'\n} catch (org.hibernate.query.sqm.StrictJpaComplianceViolation e) {\n    return em.createQuery(stripFetchAliases(hql), Employee.class).getResultList(); // 'join fetch e.tasks'\n}","preventionTips":["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"],"tags":["hql","jpa-compliance","fetch-join","hibernate"],"backgroundTag":"strict-jpa-compliance-violation","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}