hibernate/hibernate-orm · error · AnnotationException
Class '<className>' is not the root class of an entity inher
Error message
Class '<className>' is not the root class of an entity inheritance hierarchy and may not be annotated '@DiscriminatorOptions'
What it means
DiscriminatorOptionsBinder's type-level bind only executes for RootClass mappings. Annotating any non-root PersistentClass — i.e., an entity subclass in an inheritance hierarchy — falls into the else branch and throws an AnnotationException, because discriminator behavior is defined once on the hierarchy root.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/binder/internal/DiscriminatorOptionsBinder.java:34
* Handles {@link DiscriminatorOptions} annotations.
*
* @author Gavin King
*
* @since 6.5
*/
public class DiscriminatorOptionsBinder implements TypeBinder<DiscriminatorOptions> {
@Override
public void bind(DiscriminatorOptions options, MetadataBuildingContext context, PersistentClass persistentClass) {
if ( persistentClass instanceof RootClass rootClass ) {
if ( !rootClass.hasDiscriminator() ) {
throw new AnnotationException( "Root entity '" + rootClass.getEntityName()
+ "' is annotated '@DiscriminatorOptions' but has no discriminator column" );
}
rootClass.setForceDiscriminator( options.force() );
rootClass.setDiscriminatorInsertable( options.insert() );
}
else {
throw new AnnotationException("Class '" + persistentClass.getClassName()
+ "' is not the root class of an entity inheritance hierarchy and may not be annotated '@DiscriminatorOptions'");
}
}
@Override
public void bind(DiscriminatorOptions options, MetadataBuildingContext context, Component embeddableClass) {
throw new AnnotationException("Class '" + embeddableClass.getComponentClassName()
+ "' is an '@Embeddable' type and may not be annotated '@DiscriminatorOptions'");
}
}
View on GitHub (pinned to fad1729dce)
Solutions
- Move @DiscriminatorOptions to the root @Entity of the hierarchy.
- Verify the root also has a discriminator column (@DiscriminatorColumn), otherwise the companion 'no discriminator column' error follows.
- Delete the annotation if it was unintentional.
Example fix
// before
@Entity
@DiscriminatorOptions(force = true)
public class CreditCard extends BillingDetails { ... }
// after
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorOptions(force = true) // on the root only
public abstract class BillingDetails { ... }
@Entity
public class CreditCard extends BillingDetails { ... } Defensive patterns
Strategy: validation
Validate before calling
if (cls.isAnnotationPresent(org.hibernate.annotations.DiscriminatorOptions.class)) {
for (Class<?> s = cls.getSuperclass(); s != null && s != Object.class; s = s.getSuperclass()) {
if (s.isAnnotationPresent(jakarta.persistence.Entity.class)) {
throw new IllegalStateException("@DiscriminatorOptions on non-root " + cls.getName());
}
}
} Try / catch
Catch org.hibernate.AnnotationException during bootstrap; the message names the class. Abort startup.
Prevention
- Place inheritance-level annotations only on the hierarchy root.
- Avoid per-subclass annotation templates carrying root-level options.
- Lint entity hierarchies during build.
When it happens
Trigger: An entity class that extends another entity (a Subclass mapping) is annotated @DiscriminatorOptions; the binder receives the subclass mapping instead of RootClass and rejects it.
Common situations: Per-subclass annotations added 'to be safe'; entity templates that carry the annotation being used for subclasses; refactoring that moves root-level annotations down the hierarchy.
Related errors
- Root entity '<entityName>' is annotated '@DiscriminatorOptio
- Class '<componentClassName>' is an '@Embeddable' type and ma
- Class '<componentClassName>' is an '@Embeddable' type and ma
- Property '<propertyName>' may not be annotated '@BatchSize'
- One to many association '<propertyName>' was annotated '@Col
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/a61282774dcda4a5.
Report an issue: GitHub.