hibernate/hibernate-orm · error · AnnotationException

Property '<propertyName>' may not be annotated '@BatchSize'

Error message

Property '<propertyName>' may not be annotated '@BatchSize'

What it means

When @BatchSize sits on a property, BatchSizeBinder accepts only Collection values (lazy collection batch fetching). For any other property shape — @Basic, @ManyToOne, @OneToOne, @Embedded — the value is not a Collection and binding throws an AnnotationException naming the property. To-one batch fetching is configured on the target entity class, not the attribute.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/binder/internal/BatchSizeBinder.java:44

	@Override
	public void bind(BatchSize batchSize, MetadataBuildingContext context, PersistentClass persistentClass) {
		persistentClass.setBatchSize( batchSize.size() );
	}

	@Override
	public void bind(BatchSize batchSize, MetadataBuildingContext context, Component embeddableClass) {
		throw new AnnotationException("Class '" + embeddableClass.getComponentClassName()
				+ "' is an '@Embeddable' type and may not be annotated '@BatchSize'");
	}

	@Override
	public void bind(BatchSize batchSize, MetadataBuildingContext context, PersistentClass persistentClass, Property property) {
		final Value value = property.getValue();
		if ( value instanceof Collection collection ) {
			collection.setBatchSize( batchSize.size() );
		}
		else {
			throw new AnnotationException("Property '" + property.getName() + "' may not be annotated '@BatchSize'");
		}
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. For lazy @ManyToOne/@OneToOne batch fetching, move @BatchSize to the referenced @Entity class.
  2. For @OneToMany/@ManyToMany/@ElementCollection, keep @BatchSize on the collection property — that is the supported case.
  3. Otherwise delete the @BatchSize annotation from the property.

Example fix

// before
@Entity
public class Order {
    @BatchSize(size = 20)
    @ManyToOne(fetch = FetchType.LAZY)
    private User user;
}

// after
@Entity
@BatchSize(size = 20) // applies when loading Users
public class User { ... }

@Entity
public class Order {
    @ManyToOne(fetch = FetchType.LAZY)
    private User user;
}
Defensive patterns

Strategy: validation

Validate before calling

for (Field f : cls.getDeclaredFields()) {
    if (f.isAnnotationPresent(org.hibernate.annotations.BatchSize.class)
            && !java.util.Collection.class.isAssignableFrom(f.getType())) {
        throw new IllegalStateException("@BatchSize only allowed on collection properties: "
                + cls.getName() + "." + f.getName());
    }
}

Try / catch

Catch org.hibernate.AnnotationException around SessionFactory build; the message names the property. Fail fast — this is a mapping defect, not a runtime condition to recover from.

Prevention

When it happens

Trigger: Annotating a non-collection attribute with @BatchSize, most commonly @ManyToOne(fetch = LAZY) or @OneToOne; at SessionFactory build the attribute binder sees a non-Collection Value and throws.

Common situations: Trying to enable batch fetching for lazy to-one associations by annotating the attribute (the annotation belongs on the referenced entity class); confusing entity-level with collection-level @BatchSize; stale annotation after changing an association's cardinality.

Related errors


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