{"record":{"id":"e8c02b868c587b05","repo":"hibernate/hibernate-orm","slug":"property-property-defines-a-collection-table","errorCode":null,"errorMessage":"Property '${property}' defines a collection table '${collectionTable}' in the aggregate component class '${componentClassName}' within an array property, which is not allowed.","messagePattern":"Property '(.+?)' defines a collection table '(.+?)' in the aggregate component class '(.+?)' within an array property, which is not allowed\\.","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/AggregateComponentSecondPass.java","lineNumber":213,"sourceCode":"\t\t\t\t\t\t\t\t\t+ \"' in the aggregate component class '\"\n\t\t\t\t\t\t\t\t\t+ component.getComponentClassName()\n\t\t\t\t\t\t\t\t\t+ \"' within an array property, which is not allowed.\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( value instanceof Collection collection ) {\n\t\t\t\tif ( inArray && collection.getMappedByProperty() != null ) {\n\t\t\t\t\tthrow new AnnotationException(\n\t\t\t\t\t\t\t\"Property '\" + qualify( basePath, property.getName() )\n\t\t\t\t\t\t\t\t\t+ \"' uses *-to-many mapping with mappedBy '\"\n\t\t\t\t\t\t\t\t\t+ collection.getMappedByProperty()\n\t\t\t\t\t\t\t\t\t+ \"' in the aggregate component class '\"\n\t\t\t\t\t\t\t\t\t+ component.getComponentClassName()\n\t\t\t\t\t\t\t\t\t+ \"' within an array property, which is not allowed.\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif ( inArray && collection.getCollectionTable() != null ) {\n\t\t\t\t\tthrow new AnnotationException(\n\t\t\t\t\t\t\t\"Property '\" + qualify( basePath, property.getName() )\n\t\t\t\t\t\t\t\t\t+ \"' defines a collection table '\"\n\t\t\t\t\t\t\t\t\t+ collection.getCollectionTable()\n\t\t\t\t\t\t\t\t\t+ \"' in the aggregate component class '\"\n\t\t\t\t\t\t\t\t\t+ component.getComponentClassName()\n\t\t\t\t\t\t\t\t\t+ \"' within an array property, which is not allowed.\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean isAggregateArray() {\n\t\treturn switch ( component.getAggregateColumn().getSqlTypeCode( context.getMetadataCollector() ) ) {\n\t\t\tcase SqlTypes.STRUCT_ARRAY,\n\t\t\t\tSqlTypes.STRUCT_TABLE,\n\t\t\t\tSqlTypes.JSON_ARRAY,\n\t\t\t\tSqlTypes.XML_ARRAY,","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AggregateComponentSecondPass.java#L195-L231","documentation":"The third array-aggregate restriction in AggregateComponentSecondPass.validateComponent: a Collection property inside an in-array component must not define its own collection table (getCollectionTable() != null). Structs live inside the array column, so a join table for a nested collection has no valid home and binding is rejected.","triggerScenarios":"An embeddable used inside an array property contains @ElementCollection with @CollectionTable (or any collection whose table was set), so the second pass sees a collection table inside an in-array component.","commonSituations":"Adding @ElementCollection fields to an existing struct embeddable; migrating a component model into arrays without pruning nested collections; porting JPA models where embeddables were loosely authored.","solutions":["Move the nested collection out of the embeddable to the owning entity as its own collection","If the nested data belongs to the struct, store it as a nested aggregate array (struct array inside struct) where supported","Otherwise drop the field from the aggregate"],"exampleFix":"// before: collection table inside an array aggregate\n@Embeddable\npublic class TagGroup {\n    @ElementCollection\n    @CollectionTable(name = \"tag_group_tags\")   // rejected within array aggregate\n    private List<String> tags;\n}\n\n@Entity\npublic class Article {\n    @Array\n    private List<TagGroup> tagGroups;\n}\n\n// after: collection lifted to the entity\n@Entity\npublic class Article {\n    @Array\n    private List<TagGroup> tagGroups;   // TagGroup now holds only scalar fields\n\n    @ElementCollection\n    @CollectionTable(name = \"article_tags\")\n    private List<String> tags;\n}","handlingStrategy":"validation","validationCode":"// Before boot: embeddables bound for arrays must not declare collection tables\nstatic boolean aggregateHasNoCollectionTables(Class<?> embeddable) {\n    for (Field f : embeddable.getDeclaredFields()) {\n        if (f.isAnnotationPresent(ElementCollection.class)\n                || f.isAnnotationPresent(OneToMany.class)\n                || f.isAnnotationPresent(ManyToMany.class)) {\n            return false;\n        }\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n    final SessionFactory sf = new MetadataSources(standardServiceRegistry)\n            .addAnnotatedClass(MyEntity.class)\n            .buildMetadata()\n            .buildSessionFactory();\n} catch (AnnotationException | MappingException e) {\n    throw new IllegalStateException(\"Invalid ORM mapping, aborting startup: \" + e.getMessage(), e);\n}","preventionTips":["Model structs as flat value objects: scalars and nested aggregates only","Collections always belong to entities, never to embeddables stored in arrays","Add an architecture test (e.g. ArchUnit) forbidding association annotations inside @Embeddable classes used in arrays"],"tags":["hibernate","struct","aggregate","element-collection","collection-table","array","mapping"],"backgroundTag":"aggregate-struct-association-restriction","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}