hibernate/hibernate-orm · error · AnnotationException

Attribute '${attribute}' is declared as an '@Id' or '@Embedd

Error message

Attribute '${attribute}' is declared as an '@Id' or '@EmbeddedId' property by '${declaringType}' and so '${respecifyingType}' may not respecify the generation strategy

What it means

When an @Id is already declared by a superclass (typically a @MappedSuperclass) and a subclass re-specifies the generation strategy — via @GeneratedValue or an @IdGeneratorType-meta-annotated generator — Hibernate rejects the override: generation settings may only be declared where the identifier is declared. This is a deliberate limitation (see the TODO in the source) because reliably detecting a legitimate root-entity override is not currently possible.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java:773

			collector.addPropertyAnnotatedWithMapsId( ownerType.determineRawClass(), propertyAnnotatedElement );
		}

		return idPropertyCounter;
	}

	private static void checkIdProperty(MemberDetails property, PropertyData propertyData, ModelsContext context) {
		final boolean incomingIdProperty = hasIdAnnotation( property );
		if ( incomingIdProperty ) {
			final var memberDetails = propertyData.getAttributeMember();
			final boolean existingIdProperty = hasIdAnnotation( memberDetails );
			if ( existingIdProperty ) {
				if ( property.hasDirectAnnotationUsage( GeneratedValue.class )
						|| !property.getMetaAnnotated( IdGeneratorType.class, context ).isEmpty() ) {
					//TODO: it would be nice to allow a root @Entity to override an
					//      @Id field declared by a @MappedSuperclass and change the
					//      generator, but for now we don't seem to be able to detect
					//      that case here
					throw new AnnotationException(
							"Attribute '" + memberDetails.getName()
							+ "' is declared as an '@Id' or '@EmbeddedId' property by '"
							+ memberDetails.getDeclaringType().getName()
							+ "' and so '" + property.getDeclaringType().getName()
							+ "' may not respecify the generation strategy" );
				}
			}
			else {
				//TODO: it would be nice to allow a root @Entity to override a
				//      field declared by a @MappedSuperclass, redeclaring it
				//      as an @Id field, but for now we don't seem to be able
				//      to detect that case here
				throw new AnnotationException(
						"Attribute '" + memberDetails.getName()
						+ "' is declared by '" + memberDetails.getDeclaringType().getName()
						+ "' and may not be redeclared as an '@Id' or '@EmbeddedId' by '"
						+ property.getDeclaringType().getName() + "'" );
			}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Remove @GeneratedValue (and any @IdGeneratorType annotation) from the subclass override — keep the generator only on the superclass declaration.
  2. If subclasses need different strategies, declare the @Id (with its generator) separately in each concrete root entity instead of inheriting one declaration.
  3. Use a shared @MappedSuperclass WITHOUT @GeneratedValue and put @GeneratedValue on each entity's own @Id if it must redeclare; ensure only one level declares the generator.

Example fix

// before
@MappedSuperclass
public abstract class BaseEntity {
    @Id
    protected Long id;
}
@Entity
public class Order extends BaseEntity {
    @Override
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)  // rejected
    public Long getId() { return id; }
}

// after: generator lives with the declaration
@MappedSuperclass
public abstract class BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    protected Long id;
}
Defensive patterns

Strategy: validation

Validate before calling

// A subclass override of an inherited @Id must not add a generator
for (Class<?> entity : annotatedClasses) {
    Class<?> sup = entity.getSuperclass();
    while (sup != null && sup.isAnnotationPresent(MappedSuperclass.class)) {
        for (Field supF : sup.getDeclaredFields()) {
            if (!supF.isAnnotationPresent(Id.class)) continue;
            try {
                Field own = entity.getDeclaredField(supF.getName());
                if (own.isAnnotationPresent(GeneratedValue.class)) {
                    throw new IllegalStateException(entity.getName() + " may not respecify generator for inherited id " + own.getName());
                }
            } catch (NoSuchFieldException ignored) { }
        }
        sup = sup.getSuperclass();
    }
}

Try / catch

try {
    SessionFactory sf = cfg.buildSessionFactory();
} catch (AnnotationException e) {
    throw new IllegalStateException("Id generation redefinition: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: A @MappedSuperclass Base declares @Id Long id; the concrete entity redeclares the field with @Id @GeneratedValue(strategy = ...), or adds @GeneratedValue / a custom @IdGeneratorType annotation on the overriding attribute; @AttributeOverride-style field overrides that carry a generator.

Common situations: Abstract base entities with a shared @Id where some subclasses want IDENTITY and others SEQUENCE; introducing a generator on a subclass after inheriting the plain id; framework patterns (Spring Data reference templates) that re-annotate inherited id fields.

Related errors


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