hibernate/hibernate-orm · error · AnnotationException
Member '" + memberDetails.getName() + "' of embeddable class
Error message
Member '" + memberDetails.getName() + "' of embeddable class '" + propertyHolder.getClassName() + "' is annotated '@Id'
What it means
When an embeddable is used as a regular @Embedded (not as an @EmbeddedId aggregate), none of its members may carry @Id — identifiers may only appear at the entity level or inside an @EmbeddedId component. PropertyBinder detects the stray @Id while binding the property and fails bootstrap.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java:869
}
}
private static void buildProperty(
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
EntityBinder entityBinder,
boolean isIdentifierMapper,
boolean isComponentEmbedded,
boolean inSecondPass,
MetadataBuildingContext context,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass) {
final var memberDetails = inferredData.getAttributeMember();
if ( isPropertyOfRegularEmbeddable( propertyHolder, isComponentEmbedded )
&& isSimpleId( memberDetails ) ) {
throw new AnnotationException("Member '" + memberDetails.getName()
+ "' of embeddable class '" + propertyHolder.getClassName() + "' is annotated '@Id'");
}
final var attributeTypeDetails =
memberDetails.isPlural()
? memberDetails.getType()
: inferredData.getClassOrElementType();
final var propertyBinder = propertyBinder(
propertyHolder,
inferredData,
entityBinder,
isIdentifierMapper,
context,
inheritanceStatePerClass,
attributeTypeDetails
);
View on GitHub (pinned to fad1729dce)
Solutions
- Remove @Id from the member of the embeddable when the class is used via @Embedded.
- If both usages are needed, split into two classes: one clean embeddable, one @EmbeddedId-specific component.
- If the field really is the entity's identity, use @EmbeddedId on the entity property instead of @Embedded.
Example fix
// before
@Embeddable
public class Money {
@Id // rejected in a regular embeddable
private Long id;
private BigDecimal amount;
}
@Entity
public class Deal {
@Embedded
private Money price;
}
// after
@Embeddable
public class Money {
private BigDecimal amount;
}
@Entity
public class Deal {
@Id @GeneratedValue
private Long id;
@Embedded
private Money price;
} Defensive patterns
Strategy: validation
Validate before calling
// Regular embeddables must not contain @Id members
for (Class<?> cls : annotatedClasses) {
if (!cls.isAnnotationPresent(Embeddable.class)) continue;
boolean usedAsEmbeddedId = annotatedClasses.stream()
.flatMap(e -> Stream.of(e.getDeclaredFields()))
.filter(f -> f.isAnnotationPresent(Embedded.class))
.anyMatch(f -> f.getType().equals(cls));
if (usedAsEmbeddedId) continue;
for (Field f : cls.getDeclaredFields()) {
if (f.isAnnotationPresent(Id.class)) {
throw new IllegalStateException("Regular embeddable " + cls.getName() + " has @Id member " + f.getName());
}
}
} Try / catch
try {
SessionFactory sf = cfg.buildSessionFactory();
} catch (AnnotationException e) {
throw new IllegalStateException("Embeddable contains @Id: " + e.getMessage(), e);
} Prevention
- Don't reuse one embeddable class for both @Embedded and @EmbeddedId roles
- Strip @Id from embeddable members when converting @EmbeddedId to @Embedded
When it happens
Trigger: A class reused both as @EmbeddedId component and as plain @Embeddable, with @Id left inside; an embeddable copied from an id-class template; converting an @EmbeddedId to @Embedded without removing the @Id from the embedded field.
Common situations: Sharing one embeddable class between composite-id usage and regular embedded usage; refactoring from composite keys to surrogate keys; scaffolding tools that generate @Id on embeddable members.
Related errors
- Property '${property}' is annotated '@OrderBy' but is not of
- Property '${property}' is annotated '@${annotation}' but is
- Property '${property}' is annotated '@OptimisticLock(exclude
- Attribute '${attribute}' is declared as an '@Id' or '@Embedd
- Attribute '" + memberDetails.getName() + "' is declared by '
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/1f622eeb4c92e7dd.
Report an issue: GitHub.