hibernate/hibernate-orm · error · AnnotationException

Class '<componentClassName>' is an '@Embeddable' type and ma

Error message

Class '<componentClassName>' is an '@Embeddable' type and may not be annotated '@BatchSize'

What it means

Thrown by BatchSizeBinder when a class mapped as an @Embeddable is annotated @BatchSize. Hibernate 6.5+ validates annotation placement via dedicated binders, and batch fetching only applies to entity classes and collection roles. An embeddable is always loaded together with its owning entity, so a batch size on it is meaningless and fails metadata binding at SessionFactory build time with an AnnotationException.

Source

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

import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;

/**
 * Binder for the {@link BatchSize} annotation.
 *
 * @since 6.5
 *
 * @author Gavin King
 */
public class BatchSizeBinder implements TypeBinder<BatchSize>, AttributeBinder<BatchSize> {
	@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. Remove @BatchSize from the @Embeddable class.
  2. To batch-fetch the owning entities, put @BatchSize(size = N) on the owning @Entity class instead.
  3. If the embeddable declares a lazy collection, put @BatchSize on that collection-typed property (allowed for Collection values).

Example fix

// before
@Embeddable
@BatchSize(size = 10)
public class Address { ... }

// after
@Embeddable
public class Address { ... }

@Entity
@BatchSize(size = 10) // batch-fetch the owning entities
public class User {
    @Embedded
    private Address address;
}
Defensive patterns

Strategy: validation

Validate before calling

for (Class<?> cls : persistentClasses) {
    if (cls.isAnnotationPresent(jakarta.persistence.Embeddable.class)
            && cls.isAnnotationPresent(org.hibernate.annotations.BatchSize.class)) {
        throw new IllegalStateException("@BatchSize not allowed on @Embeddable " + cls.getName());
    }
}

Try / catch

Wrap SessionFactory bootstrap (metadata.buildSessionFactory()) in try/catch (org.hibernate.AnnotationException e): the message names the offending class; log it and abort startup. Never catch-and-continue — the metadata is unusable.

Prevention

When it happens

Trigger: Registering an @Embeddable class that carries @BatchSize (via MetadataSources.addAnnotatedClass, addPackage, or annotation scanning) and building the SessionFactory. The type-binder overload bind(BatchSize, context, Component) runs and unconditionally throws.

Common situations: Copying @BatchSize onto a shared embeddable (e.g. Address) while refactoring an entity; a leftover annotation after converting an @Entity into an @Embeddable; upgrading to Hibernate 6.5+ where the previously ignored annotation now aborts bootstrap.

Related errors


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