{"record":{"id":"d54ef23624330760","repo":"hibernate/hibernate-orm","slug":"cannot-simultaneously-fetch-multiple-bags","errorCode":null,"errorMessage":"cannot simultaneously fetch multiple bags: {}","messagePattern":"cannot simultaneously fetch multiple bags: (.+?)","errorType":"exception","errorClass":"MultipleBagFetchException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java","lineNumber":9091,"sourceCode":"\t\t\t\t\t\t\tif ( entityMappingType.getSuperMappingType() != null ) {\n\t\t\t\t\t\t\t\t// A joined table group was created by an enabled entity graph or fetch profile,\n\t\t\t\t\t\t\t\t// and it's of an inheritance subtype, so we should apply the discriminator\n\t\t\t\t\t\t\t\tgetCurrentClauseStack().push( Clause.FROM );\n\t\t\t\t\t\t\t\tregisterEntityNameUsage( actualTableGroup, EntityNameUse.TREAT,\n\t\t\t\t\t\t\t\t\t\tentityMappingType.getEntityName() );\n\t\t\t\t\t\t\t\tgetCurrentClauseStack().pop();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( fetchable instanceof PluralAttributeMapping pluralAttributeMapping ) {\n\t\t\t\t\t\tfinal var collectionClassification =\n\t\t\t\t\t\t\t\tpluralAttributeMapping.getMappedType()\n\t\t\t\t\t\t\t\t\t\t.getCollectionSemantics()\n\t\t\t\t\t\t\t\t\t\t.getCollectionClassification();\n\t\t\t\t\t\tif ( collectionClassification == CollectionClassification.BAG ) {\n\t\t\t\t\t\t\tfinal var navigableRole = fetchable.getNavigableRole();\n\t\t\t\t\t\t\tif ( currentBagRole != null ) {\n\t\t\t\t\t\t\t\tthrow new MultipleBagFetchException(\n\t\t\t\t\t\t\t\t\t\tArrays.asList( currentBagRole,\n\t\t\t\t\t\t\t\t\t\t\t\tnavigableRole.getNavigableName() )\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcurrentBagRole = navigableRole.getNavigableName();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn fetch;\n\t\t}\n\t\tfinally {\n\t\t\tif ( incrementFetchDepth ) {\n\t\t\t\tfetchDepth--;\n\t\t\t}\n\t\t\tif ( entityGraphTraversalState != null && traversalResult != null ) {\n\t\t\t\tentityGraphTraversalState.backtrack( traversalResult );\n\t\t\t}","sourceCodeStart":9073,"sourceCodeEnd":9109,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java#L9073-L9109","documentation":"The classic Hibernate multiple-bag limitation: while resolving fetches, two or more fetched plural attributes with CollectionClassification.BAG (a java.util.List mapping without @OrderColumn) were encountered on one query. Join-fetching multiple bags would require an ambiguous cartesian row product, so Hibernate aborts with MultipleBagFetchException naming the two conflicting roles.","triggerScenarios":"HQL 'from X x join fetch x.list1 join fetch x.list2' where both are List mappings without @OrderColumn; entity graphs that fetch two bag associations; criteria roots with two fetch(..., JOIN) calls on bag attributes; cascading fetch of a bag inside another fetched association.","commonSituations":"Entities modeled with List everywhere (default OneToMany target); adding a second lazy collection and fetching it for a screen; entity graphs inherited from modules that later add bags; performance tuning that blindly converts lazy loads to join fetch.","solutions":["Add @OrderColumn to the List mappings - that classifies them as LIST, which can be fetched together","Change one or both collections to Set (with LinkedHashSet/TreeSet to keep ordering)","Split into two queries: fetch the parent list first, then load each bag separately (or via batch/subselect fetching)","Keep the second bag lazy and initialize it on demand, or use @Fetch(FetchMode.SUBSELECT) for the second query"],"exampleFix":"// before\n@OneToMany(mappedBy = \"o\", cascade = ALL)\nprivate List<Line> lines = new ArrayList<>();      // BAG\n@OneToMany(mappedBy = \"o\", cascade = ALL)\nprivate List<Note> notes = new ArrayList<>();     // BAG -> MultipleBagFetchException\n\n// after\n@OneToMany(mappedBy = \"o\", cascade = ALL)\n@OrderColumn(name = \"pos\")\nprivate List<Line> lines = new ArrayList<>();    // LIST - fetchable together","handlingStrategy":"validation","validationCode":"// Before executing, verify at most one fetched attribute is a BAG\norg.hibernate.metamodel.model.domain.EntityDomainType<E> et =\n    sessionFactory.getMetamodel().entity(E.class);\nlong bagFetches = fetchedAttributes.stream()\n    .filter(a -> et.getAttribute(a) instanceof jakarta.metamodel.PluralAttribute<?, ?, ?> pa\n        && pa.getCollectionType() == jakarta.metamodel.PluralAttribute.CollectionType.LIST\n        && !hasOrderColumn(a))  // hasOrderColumn: check your mapping metadata\n    .count();\nif (bagFetches > 1) {\n    throw new IllegalStateException(\"Query join-fetches multiple bags - split the query or add @OrderColumn\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return query.getResultList();\n} catch (org.hibernate.HibernateException e) {\n    if (e.getClass().getSimpleName().equals(\"MultipleBagFetchException\")) {\n        return loadInTwoQueries(id -> session.createQuery(secondaryFetchHql, Line.class)\n            .setParameter(\"ids\", id).getResultList());\n    }\n    throw e;\n}","preventionTips":["Prefer Set or @OrderColumn List for associations you will join-fetch","Fetch at most one collection per query; use batch or subselect fetching for the rest","Review entity graphs before adding new collection attributes"],"tags":["hibernate","fetch","bag","onetomany","entity-graph"],"backgroundTag":"multiple-bag-fetch-exception","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}