hibernate/hibernate-orm · error · AnnotationException

Type '{}' is annotated both '@Entity' and '@MappedSuperclass

Error message

Type '{}' is annotated both '@Entity' and '@MappedSuperclass'

What it means

The same type carries both '@Entity' and '@MappedSuperclass'. These are mutually exclusive roles: an entity is a mapped, instantiated persistent type, while a mapped superclass only contributes state/mapping to subclasses. Historically this combination produced a NullPointerException deep inside binding, so Hibernate now fails fast with this clear message.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotationBinder.java:287

		if ( context.getMetadataCollector().getClassType( classDetails ) == ENTITY ) {
			bindEntityClass( classDetails, inheritanceStatePerClass, context );
		}
	}

	private static void handleImport(ClassDetails annotatedClass, MetadataBuildingContext context) {
		if ( annotatedClass.hasDirectAnnotationUsage( Imported.class ) ) {
			final String qualifiedName = annotatedClass.getName();
			final String name = unqualify( qualifiedName );
			final String rename = annotatedClass.getDirectAnnotationUsage( Imported.class ).rename();
			context.getMetadataCollector().addImport( rename.isBlank() ? name : rename, qualifiedName );
		}
	}

	private static void detectMappedSuperclassProblems(ClassDetails annotatedClass) {
		if ( isMappedSuperclass( annotatedClass ) ) {
			// @Entity and @MappedSuperclass on the same class leads to NPE down the road
			if ( isEntity( annotatedClass ) ) {
				throw new AnnotationException( "Type '" + annotatedClass.getName()
						+ "' is annotated both '@Entity' and '@MappedSuperclass'" );
			}
			if ( annotatedClass.hasDirectAnnotationUsage( Table.class ) ) {
				throw new AnnotationException( "Mapped superclass '" + annotatedClass.getName()
						+ "' may not specify a '@Table'" );
			}
			if ( annotatedClass.hasDirectAnnotationUsage( Inheritance.class ) ) {
				throw new AnnotationException( "Mapped superclass '" + annotatedClass.getName()
						+ "' may not specify an '@Inheritance' mapping strategy" );
			}
		}
	}

	private static void bindTypeDescriptorRegistrations(
			AnnotationTarget annotatedElement,
			MetadataBuildingContext context) {
		final var managedBeanRegistry = context.getBootstrapContext().getManagedBeanRegistry();
		final var sourceModelContext = modelsContext( context );

View on GitHub (pinned to fad1729dce)

Solutions

  1. If the class should be a mapped superclass: remove '@Entity' (and any '@Table') so it only persists through subclasses.
  2. If the class should be a concrete entity: remove '@MappedSuperclass' (its state will map to its own table).
  3. For shared state across entities, keep '@MappedSuperclass' on an abstract base and put '@Entity' on each leaf.

Example fix

// before
@Entity            // both present
@MappedSuperclass  // -> AnnotationException
public abstract class BaseEntity { @Id Long id; }

// after
@MappedSuperclass
public abstract class BaseEntity {
    @Id @GeneratedValue
    Long id;
}

@Entity
class Customer extends BaseEntity { String name; }
Defensive patterns

Strategy: validation

Validate before calling

// Guard: a type may not be both @Entity and @MappedSuperclass
if (cls.isAnnotationPresent(MappedSuperclass.class)
        && cls.isAnnotationPresent(Entity.class)) {
    throw new IllegalStateException(
        cls.getName() + " is annotated both @Entity and @MappedSuperclass");
}

Try / catch

try {
    Metadata md = sources.buildMetadata();
} catch (AnnotationException e) {
    // 'annotated both @Entity and @MappedSuperclass' -> keep exactly one role
    throw newConfigurationException("Conflicting type-level annotations", e);
}

Prevention

When it happens

Trigger: A class annotated '@Entity' (or otherwise processed as an entity) that also has a direct '@MappedSuperclass' annotation — often when someone converts an entity into a base class and leaves '@Entity' behind, or generates mappings that stamp both on.

Common situations: Refactoring an existing entity into a reusable superclass; annotation generators/processors emitting both; copy-paste of a template class that already had @MappedSuperclass.

Related errors


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