hibernate/hibernate-orm · error · AnnotationException

Attribute '" + memberDetails.getName() + "' is declared by '

Error message

Attribute '" + memberDetails.getName() + "' is declared by '" + memberDetails.getDeclaringType().getName() + "' and may not be redeclared as an '@Id' or '@EmbeddedId' by '" + property.getDeclaringType().getName() + "'

What it means

The mirror case of the generation-strategy error: an attribute that a superclass declares as a plain (non-id) property is redeclared in a subclass as @Id or @EmbeddedId. Hibernate's id-property bookkeeping cannot promote an inherited attribute to identifier status, so AnnotationException is thrown while reconciling id properties.

Source

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

						|| !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() + "'" );
			}
		}
	}

	static boolean hasIdAnnotation(MemberDetails element) {
		return isSimpleId( element ) || isEmbeddedId( element );
	}

	/**
	 * Process annotation of a particular property or field.
	 */
	public static void processElementAnnotations(
			PropertyHolder propertyHolder,
			Nullability nullability,

View on GitHub (pinned to fad1729dce)

Solutions

  1. Remove @Id/@EmbeddedId from the subclass redeclaration.
  2. If the subtype truly needs its own identity, remove the conflicting attribute from the superclass and declare the @Id field on the subclass (or split the hierarchy).
  3. Restructure so the identifier is declared at one level only: either the shared root (@MappedSuperclass/@Entity) or the concrete entity — never promoted from below.

Example fix

// before
@MappedSuperclass
public abstract class Base {
    protected String code;
}
@Entity
public class Product extends Base {
    @Override
    @Id   // promoting an inherited plain attribute => rejected
    public String getCode() { return code; }
}

// after: declare the id at one level
@MappedSuperclass
public abstract class Base { /* no 'code' here */ }
@Entity
public class Product extends Base {
    @Id
    private String code;
}
Defensive patterns

Strategy: validation

Validate before calling

// A subclass may not promote an inherited plain attribute to @Id
for (Class<?> entity : annotatedClasses) {
    Class<?> sup = entity.getSuperclass();
    while (sup != null && (sup.isAnnotationPresent(MappedSuperclass.class) || sup.isAnnotationPresent(Entity.class))) {
        for (Field own : entity.getDeclaredFields()) {
            if (!own.isAnnotationPresent(Id.class)) continue;
            try {
                Field supF = sup.getDeclaredField(own.getName());
                if (!supF.isAnnotationPresent(Id.class)) {
                    throw new IllegalStateException(entity.getName() + " redeclares inherited attribute " + own.getName() + " as @Id");
                }
            } catch (NoSuchFieldException ignored) { }
        }
        sup = sup.getSuperclass();
    }
}

Try / catch

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

Prevention

When it happens

Trigger: @MappedSuperclass declares private String code; the subclass adds @Id on its overriding code field/getter; hierarchies where a subtype was supposed to own its primary key but the field already exists higher up.

Common situations: Refactoring single-table hierarchies so a subtype becomes independently identifiable; merging unrelated entities under a common base class; generated code that adds @Id to every concrete class's fields.

Related errors


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