{"record":{"id":"9e617d3605fe2248","repo":"hibernate/hibernate-orm","slug":"cannot-combine-other-returns-with-a-collection-ret","errorCode":null,"errorMessage":"Cannot combine other returns with a collection return (\" + registrationName + \")","messagePattern":"Cannot combine other returns with a collection return \\(\" \\+ registrationName \\+ \"\\)","errorType":"exception","errorClass":"MappingException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/query/HbmResultSetMappingDescriptor.java","lineNumber":147,"sourceCode":"\t\t\t\t);\n\t\t\t\tlocalResultDescriptors.add( collectionResultDescriptor );\n\t\t\t\tfetchParentByAlias.put( collectionResultDescriptor.tableAlias, collectionResultDescriptor );\n\t\t\t}\n\t\t\telse if ( hbmValueMapping instanceof JaxbHbmNativeQueryJoinReturnType jaxbHbmJoinReturn ) {\n\t\t\t\tcollectJoinFetch( jaxbHbmJoinReturn, joinDescriptors, fetchParentByAlias, registrationName, context );\n\t\t\t}\n\t\t\telse if ( hbmValueMapping instanceof JaxbHbmNativeQueryScalarReturnType hbmScalarReturn ) {\n\t\t\t\tlocalResultDescriptors.add( new ScalarDescriptor( hbmScalarReturn ) );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"Unknown NativeQueryReturn type: \" + hbmValueMapping.getClass().getName()\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif ( foundCollectionReturn && localResultDescriptors.size() > 1 ) {\n\t\t\tthrow new MappingException(\n\t\t\t\t\t\"Cannot combine other returns with a collection return (\" + registrationName + \")\"\n\t\t\t);\n\t\t}\n\n\t\tthis.resultDescriptors = localResultDescriptors;\n\t}\n\n\tpublic static void collectJoinFetch(\n\t\t\tJaxbHbmNativeQueryJoinReturnType jaxbHbmJoin,\n\t\t\tMap<String, Map<String, JoinDescriptor>> joinDescriptors,\n\t\t\tMap<String, HbmFetchParent> fetchParentByAlias,\n\t\t\tString registrationName,\n\t\t\tMetadataBuildingContext context) {\n\t\t// property path is in the form {ownerAlias}.{joinedPath}. Split it into the 2 parts.\n\t\tfinal String fullPropertyPath = jaxbHbmJoin.getProperty();\n\t\tfinal int firstDot = fullPropertyPath.indexOf( '.' );\n\t\tif ( firstDot < 1 ) {\n\t\t\tthrow new MappingException(","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/query/HbmResultSetMappingDescriptor.java#L129-L165","documentation":"Hibernate throws this MappingException while building an HBM native-query result set mapping when a <return-collection/> is combined with any other entity <return/> or <return-scalar/> return. The HbmResultSetMappingDescriptor constructor tracks foundCollectionReturn and rejects a descriptor list larger than one when a collection return is present, because a collection mapping must be the sole root return. Only <return-join/> entries may accompany it.","triggerScenarios":"An hbm.xml <resultset> or <sql-query> element containing <return-collection/> together with a <return alias=\"...\" class=\"...\"/> or a <return-column/>/<return-scalar/> entry; migrating a legacy .hbm.xml file where extra returns were appended over time.","commonSituations":"Copy-pasting an entity return block into a collection-based native query mapping; upgrading old Hibernate 3/4 mappings to Hibernate 6 where the constraint is enforced at descriptor build time; attempting to fetch both the collection and its owner as separate root returns.","solutions":["Remove the entity <return/> and <return-scalar/> entries so the <return-collection/> is the only root return.","If you need the owning entity, keep only <return alias=\"o\" class=\"Order\"/> and reach the collection via <return-join alias=\"i\" property=\"o.items\"/> instead.","If scalar values are needed alongside the collection, return them via a different query or a second result set mapping."],"exampleFix":"<!-- before -->\n<sql-query name=\"ordersWithItems\">\n    <return alias=\"o\" class=\"Order\"/>\n    <return-collection alias=\"i\" role=\"Order.items\"/>\n</sql-query>\n\n<!-- after: collection is the sole root return -->\n<sql-query name=\"ordersWithItems\">\n    <return-collection alias=\"i\" role=\"Order.items\"/>\n    <return-join alias=\"o\" property=\"i.element.order\"/>\n</sql-query>","handlingStrategy":"validation","validationCode":"// before building the SessionFactory, inspect the parsed <sql-query>/<resultset> nodes:\n// List<String> returnKinds = returns.stream().map(r -> r.elementName()).toList();\nboolean hasCollectionReturn = returnKinds.contains( \"return-collection\" );\nlong roots = returnKinds.stream().filter( k -> k.equals( \"return\" ) || k.equals( \"return-column\" ) || k.equals( \"return-scalar\" ) ).count();\nif ( hasCollectionReturn && roots > 0 ) {\n    throw new IllegalStateException( \"Collection return must be the sole root return in mapping \" + name );\n}","typeGuard":null,"tryCatchPattern":"try {\n    sessionFactory = metadata.buildSessionFactory();\n}\ncatch ( MappingException e ) {\n    if ( e.getMessage().startsWith( \"Cannot combine other returns with a collection return\" ) ) {\n        // fix the named mapping; message contains the registration name\n    }\n    throw e;\n}","preventionTips":["Keep one root return per native query mapping; model associated data with <return-join/>.","Add an XML lint step (or unit test that boots the metadata) to CI for every hbm.xml file.","Prefer annotation-based @SqlResultSetMapping for new code where the structure is checked more strictly by the compiler."],"tags":["hibernate","hbm-xml","native-query","result-set-mapping","boot"],"backgroundTag":"result-set-mapping-conflict","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}