hibernate/hibernate-orm · error · AnnotationException

Property '${property}' defines a collection table '${collect

Error message

Property '${property}' defines a collection table '${collectionTable}' in the aggregate component class '${componentClassName}' within an array property, which is not allowed.

What it means

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.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/AggregateComponentSecondPass.java:213

									+ "' in the aggregate component class '"
									+ component.getComponentClassName()
									+ "' within an array property, which is not allowed."
					);
				}
			}
			else if ( value instanceof Collection collection ) {
				if ( inArray && collection.getMappedByProperty() != null ) {
					throw new AnnotationException(
							"Property '" + qualify( basePath, property.getName() )
									+ "' uses *-to-many mapping with mappedBy '"
									+ collection.getMappedByProperty()
									+ "' in the aggregate component class '"
									+ component.getComponentClassName()
									+ "' within an array property, which is not allowed."
					);
				}
				if ( inArray && collection.getCollectionTable() != null ) {
					throw new AnnotationException(
							"Property '" + qualify( basePath, property.getName() )
									+ "' defines a collection table '"
									+ collection.getCollectionTable()
									+ "' in the aggregate component class '"
									+ component.getComponentClassName()
									+ "' within an array property, which is not allowed."
					);
				}
			}
		}
	}

	private boolean isAggregateArray() {
		return switch ( component.getAggregateColumn().getSqlTypeCode( context.getMetadataCollector() ) ) {
			case SqlTypes.STRUCT_ARRAY,
				SqlTypes.STRUCT_TABLE,
				SqlTypes.JSON_ARRAY,
				SqlTypes.XML_ARRAY,

View on GitHub (pinned to fad1729dce)

Solutions

  1. Move the nested collection out of the embeddable to the owning entity as its own collection
  2. If the nested data belongs to the struct, store it as a nested aggregate array (struct array inside struct) where supported
  3. Otherwise drop the field from the aggregate

Example fix

// before: collection table inside an array aggregate
@Embeddable
public class TagGroup {
    @ElementCollection
    @CollectionTable(name = "tag_group_tags")   // rejected within array aggregate
    private List<String> tags;
}

@Entity
public class Article {
    @Array
    private List<TagGroup> tagGroups;
}

// after: collection lifted to the entity
@Entity
public class Article {
    @Array
    private List<TagGroup> tagGroups;   // TagGroup now holds only scalar fields

    @ElementCollection
    @CollectionTable(name = "article_tags")
    private List<String> tags;
}
Defensive patterns

Strategy: validation

Validate before calling

// Before boot: embeddables bound for arrays must not declare collection tables
static boolean aggregateHasNoCollectionTables(Class<?> embeddable) {
    for (Field f : embeddable.getDeclaredFields()) {
        if (f.isAnnotationPresent(ElementCollection.class)
                || f.isAnnotationPresent(OneToMany.class)
                || f.isAnnotationPresent(ManyToMany.class)) {
            return false;
        }
    }
    return true;
}

Try / catch

try {
    final SessionFactory sf = new MetadataSources(standardServiceRegistry)
            .addAnnotatedClass(MyEntity.class)
            .buildMetadata()
            .buildSessionFactory();
} catch (AnnotationException | MappingException e) {
    throw new IllegalStateException("Invalid ORM mapping, aborting startup: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/e8c02b868c587b05. Report an issue: GitHub.