{"record":{"id":"0b8512f1911de160","repo":"hibernate/hibernate-orm","slug":"requested-join-fetch-with-association-s-with","errorCode":null,"errorMessage":"Requested join fetch with association [%s] with '%s' join type, but found existing join fetch with '%s' join type.","messagePattern":"Requested join fetch with association \\[(.+?)\\] with '(.+?)' join type, but found existing join fetch with '(.+?)' join type\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java","lineNumber":454,"sourceCode":"\t\t\t}\n\t\t\telse {\n\t\t\t\texpression.accept( pathVisitor );\n\t\t\t}\n\t\t}\n\t\treturn navigablePaths;\n\t}\n\n\tpublic static <T, A> SqmAttributeJoin<T, A> findCompatibleFetchJoin(\n\t\t\tSqmFrom<?, T> sqmFrom,\n\t\t\tSqmPathSource<A> pathSource,\n\t\t\tSqmJoinType requestedJoinType) {\n\t\tfor ( final var join : sqmFrom.getSqmJoins() ) {\n\t\t\tif ( join.getModel() == pathSource ) {\n\t\t\t\tfinal var attributeJoin = (SqmAttributeJoin<T, ?>) join;\n\t\t\t\tif ( attributeJoin.isFetched() ) {\n\t\t\t\t\tfinal var joinType = join.getSqmJoinType();\n\t\t\t\t\tif ( joinType != requestedJoinType ) {\n\t\t\t\t\t\tthrow new IllegalStateException( String.format(\n\t\t\t\t\t\t\t\t\"Requested join fetch with association [%s] with '%s' join type, \" +\n\t\t\t\t\t\t\t\t\t\t\"but found existing join fetch with '%s' join type.\",\n\t\t\t\t\t\t\t\tpathSource.getPathName(),\n\t\t\t\t\t\t\t\trequestedJoinType,\n\t\t\t\t\t\t\t\tjoinType\n\t\t\t\t\t\t) );\n\t\t\t\t\t}\n\t\t\t\t\t//noinspection unchecked\n\t\t\t\t\treturn (SqmAttributeJoin<T, A>) attributeJoin;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> generateJdbcParamsXref(\n\t\t\tDomainParameterXref domainParameterXref,\n\t\t\tJdbcParameterBySqmParameterAccess jdbcParameterBySqmParameterAccess) {","sourceCodeStart":436,"sourceCodeEnd":472,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java#L436-L472","documentation":"Thrown as IllegalStateException by SqmUtil.findCompatibleFetchJoin when the same association is already join-fetched but with a different SqmJoinType than the one now requested. When resolving a fetch of a path, Hibernate reuses an existing fetched attribute join only if the join type matches; a mismatch is treated as a user error because two fetches of one association with different join types have contradictory semantics (an inner fetch cannot coexist with a left fetch of the same relation).","triggerScenarios":"HQL that fetches the same association twice with different join types: 'select p from Person p left join fetch p.address join fetch p.address'; Criteria code calling root.fetch(\"address\", JoinType.LEFT) and later root.fetch(\"address\", JoinType.INNER); query builders or @EntityGraph handling that add a default inner 'join fetch' on top of an existing 'left join fetch' of the same path.","commonSituations":"One team fixes an N+1 with 'join fetch' while another layer (entity graph, repository fragment, generated query) already declared 'left join fetch' on the same association; upgrades that change default fetch join generation; criteria utilities that always apply fetches with INNER unless told otherwise.","solutions":["Make the join types identical for every fetch of the same association (use 'left join fetch' both times, or 'join fetch' both times)","Remove the duplicate fetch — a single fetch of the association already loads it","In Criteria, reuse the existing Fetch object instead of calling fetch(attribute, joinType) again for the same attribute","Audit @EntityGraph / applied graphs for a conflicting fetch of the same association and align its join type"],"exampleFix":"// before\nString hql = \"select p from Person p join fetch p.address left join fetch p.address\";\n// after\nString hql = \"select p from Person p left join fetch p.address\";","handlingStrategy":"validation","validationCode":"static void assertNoConflictingFetchJoins(FetchParent<?, ?> parent) {\n    Map<String, JoinType> seen = new HashMap<>();\n    for (Fetch<?, ?> f : parent.getFetches()) {\n        JoinType prev = seen.put(f.getAttribute().getName(), f.getJoinType());\n        if (prev != null && prev != f.getJoinType()) {\n            throw new IllegalStateException(\n                \"Conflicting join fetch types for \" + f.getAttribute().getName());\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    return session.createQuery(hql, Person.class).getResultList();\n} catch (IllegalStateException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"existing join fetch\")) {\n        // two fetches of one association with different join types: fix the query string\n        throw new IllegalArgumentException(\"Conflicting fetch join types in query: \" + hql, e);\n    }\n    throw e;\n}","preventionTips":["Search query strings for repeated 'join fetch <same path>' with different join types","Centralize the fetch strategy (and its join type) per association in one place","Reuse the existing Fetch object in criteria instead of fetching the same attribute twice"],"tags":["hibernate","join-fetch","hql","criteria","entity-graph"],"backgroundTag":"conflicting-join-fetch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}