{"record":{"id":"4e37d615d8fb4cb4","repo":"hibernate/hibernate-orm","slug":"one-to-many-collections-with-identifiers-are-not-s","errorCode":null,"errorMessage":"one-to-many collections with identifiers are not supported","messagePattern":"one-to-many collections with identifiers are not supported","errorType":"exception","errorClass":"MappingException","httpStatus":null,"severity":"critical","filePath":"hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java","lineNumber":447,"sourceCode":"\t\t\t\ti++;\n\t\t\t}\n\t\t\tindexContainsFormula = hasFormula;\n\t\t}\n\t\telse {\n\t\t\tindexContainsFormula = false;\n\t\t\tindexColumnIsGettable = null;\n\t\t\tindexColumnIsSettable = null;\n\t\t\tindexFormulaTemplates = null;\n\t\t\tindexFormulas = null;\n\t\t\tindexType = null;\n\t\t\tindexColumnNames = null;\n\t\t\tindexColumnAliases = null;\n\t\t}\n\n\t\tfinal boolean hasIdentifier = collectionBootDescriptor.isIdentified();\n\t\tif ( hasIdentifier ) {\n\t\t\tif ( collectionBootDescriptor.isOneToMany() ) {\n\t\t\t\tthrow new MappingException( \"one-to-many collections with identifiers are not supported\" );\n\t\t\t}\n\t\t\t//noinspection ConstantConditions\n\t\t\tfinal var idCollection = (IdentifierCollection) collectionBootDescriptor;\n\t\t\tidentifierType = idCollection.getIdentifier().getType();\n\t\t\tfinal var idColumn = idCollection.getIdentifier().getColumns().get(0);\n\t\t\tidentifierColumnName = idColumn.getQuotedName( dialect );\n\t\t\tidentifierColumnAlias = idColumn.getAlias( dialect );\n\t\t\tidentifierGenerator = createGenerator( creationContext, idCollection );\n\t\t}\n\t\telse {\n\t\t\tidentifierType = null;\n\t\t\tidentifierColumnName = null;\n\t\t\tidentifierColumnAlias = null;\n\t\t\tidentifierGenerator = null;\n\t\t}\n\n\t\tisLazy = collectionBootDescriptor.isLazy();\n\t\tisExtraLazy = collectionBootDescriptor.isExtraLazy();","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java#L429-L465","documentation":"In the AbstractCollectionPersister constructor, an identified collection (idbag semantics, @CollectionId / <idbag>) that is also a one-to-many is rejected with MappingException. Collection-table identifiers exist only for collections of values (element/idbag); a one-to-many stores rows in the target entity's table via FK and has no join-row identity to assign, so the combination is invalid.","triggerScenarios":"A @OneToMany field also carrying @CollectionId/@CollectionIdJdbcType (or hbm.xml <idbag> containing <one-to-many>); attempting to give the join row of an inverse one-to-many its own surrogate id.","commonSituations":"Migrating legacy HBM <idbag> mappings forward where they contained <one-to-many>; developers adding @CollectionId to a @OneToMany to get stable row ids for the collection; mixing idbag examples (written for element collections) into associations.","solutions":["Remove @CollectionId from the @OneToMany — inverse one-to-many rows live in the target table and need no collection-row identifier","If you need an independent join table with its own surrogate id and entity rows, model it as @ManyToMany or as an explicit association entity (@Entity join table with its own id and two @ManyToOne)","If you need identified rows of values (not entities), use @ElementCollection + @CollectionId (idbag) instead of @OneToMany"],"exampleFix":"// before\n@OneToMany(mappedBy = \"order\")\n@CollectionId(columns = @Column(name = \"line_id\"), generator = \"seq\", type = Long.class)\nprivate List<OrderLine> lines; // MappingException: one-to-many collections with identifiers are not supported\n\n// after\n@OneToMany(mappedBy = \"order\", cascade = CascadeType.ALL)\nprivate List<OrderLine> lines; // plain inverse collection\n// or explicit association entity if you need join-row identity:\n// @Entity class OrderLineLink { @Id Long id; @ManyToOne Order order; @ManyToOne OrderLine line; }","handlingStrategy":"validation","validationCode":"// Startup scan: @OneToMany + @CollectionId is an invalid combination per AbstractCollectionPersister\nfor (Class<?> c : scannedEntityClasses) {\n  for (java.lang.reflect.Field f : c.getDeclaredFields()) {\n    if (f.isAnnotationPresent(OneToMany.class)\n        && (f.isAnnotationPresent(org.hibernate.annotations.CollectionId.class)\n            || f.isAnnotationPresent(org.hibernate.annotations.CollectionIdJdbcType.class))) {\n      throw new IllegalStateException(\"Field \" + f + \" combines @OneToMany with a collection id, which is unsupported\");\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  sessionFactory = new Configuration().configure().buildSessionFactory();\n} catch (MappingException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"one-to-many collections with identifiers\")) {\n    throw new IllegalStateException(\"Remove @CollectionId from the @OneToMany or remodel as @ManyToMany/association entity\", e);\n  }\n  throw e;\n}","preventionTips":["Reserve @CollectionId for @ElementCollection-style idbags only, never associations","Audit legacy HBM <idbag> mappings for embedded <one-to-many> elements before migrating","Use an explicit association @Entity with its own id when join-row identity is genuinely required"],"tags":["hibernate","collection-mapping","one-to-many","idbag","bootstrap"],"backgroundTag":"invalid-collection-mapping","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}