hibernate/hibernate-orm · error · AnnotationException
Attribute '%s' of entity '%s' is mapped by association '%s'
Error message
Attribute '%s' of entity '%s' is mapped by association '%s' but is not annotated '@EmbeddedId'
What it means
Thrown by PropertyBinder.checkMappedId when a @ManyToOne/@OneToOne association uses @MapsId but the referenced entity's identifier is a composite @EmbeddedId while the dependent attribute is not itself annotated @EmbeddedId. JPA derived-identity rules require that when the parent key is an EmbeddedId, the dependent's id must be an EmbeddedId embeddable containing the mapped attribute. Bootstrap fails immediately after Hibernate resolves the referenced entity binding.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java:1513
return mapsIdProperty;
}
}
else {
return collector.getPropertyAnnotatedWithMapsId( classDetails, isId ? "" : propertyName );
}
}
private static void checkMappedId(
PropertyHolder propertyHolder,
MemberDetails property,
String propertyName, PropertyData mapsIdProperty,
InFlightMetadataCollector collector) {
final var referencedEntityName = mapsIdProperty.getClassOrElementName();
final var referencedEntityBinding = collector.getEntityBinding( referencedEntityName );
if ( referencedEntityBinding != null ) {
if ( referencedEntityBinding.getIdentifier() instanceof Component compositeId ) {
if ( !isEmbeddedId( property ) ) {
throw new AnnotationException(
"Attribute '%s' of entity '%s' is mapped by association '%s' but is not annotated '@EmbeddedId'"
.formatted(
propertyName,
propertyHolder.getPersistentClass().getEntityName(),
mapsIdProperty.getPropertyName()
)
);
}
final String expectedTypeName = compositeId.getComponentClassName();
final String actualTypeName = property.getType().getName();
if ( !hasCompatibleType( actualTypeName, expectedTypeName ) ) {
throw new AnnotationException(
"Identifier attribute '%s' of entity '%s' has type '%s' but is mapped by association '%s' to entity '%s' with composite identifier type '%s'"
.formatted(
propertyName,
propertyHolder.getPersistentClass().getEntityName(),
actualTypeName,
mapsIdProperty.getPropertyName(),View on GitHub (pinned to fad1729dce)
Solutions
- Change the child's identifier to an embeddable containing the parent key field and annotate it @EmbeddedId, keeping @MapsId on the association pointing at that field.
- If the composite parent key is unwanted, simplify the parent back to a simple @Id so MapsId pairs @Id to @Id.
- If you do not need a derived identity, drop @MapsId and map a plain FK column with @JoinColumn.
Example fix
// before (parent has @EmbeddedId ParentId)
@Id
@Column(name = "parent_id")
private Long parentId; // wrong shape: parent key is composite
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("parentId")
private Parent parent;
// after
@Embeddable
public class ChildId implements Serializable {
private ParentId parentId; // same embeddable type as parent's id
}
@EmbeddedId
private ChildId id;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("parentId")
private Parent parent; Defensive patterns
Strategy: validation
Validate before calling
// before adding classes, assert MapsId children of EmbeddedId parents use EmbeddedId
for (Class<?> parent : parentsWithEmbeddedId()) {
for (Class<?> child : entitiesMappingIdOf(parent)) {
Field f = idAttribute(child);
if (!f.isAnnotationPresent(EmbeddedId.class)) {
throw new IllegalStateException(child.getSimpleName()
+ " maps id of " + parent.getSimpleName() + " but lacks @EmbeddedId");
}
}
} Try / catch
try {
metadata = sources.buildMetadata();
} catch (AnnotationException e) {
// message names the attribute, entity and association - surface it to the mapper
failBuild("Derived identity misconfigured: " + e.getMessage());
} Prevention
- Match id shapes across @MapsId pairs: EmbeddedId parent <=> EmbeddedId child; simple parent <=> simple child.
- Write one derived-identity IT per parent/child pair in the mapping test suite.
- Prefer reusing the parent's embeddable for the child id.
When it happens
Trigger: Parent entity has @EmbeddedId ParentId id; child declares @MapsId("parentId") on the association but uses a simple @Id Long parentId, or no id at all. Fires when the referenced entity binding is already registered and getIdentifier() returns a Component (composite).
Common situations: Converting a simple-key parent to a composite key and forgetting the child's id shape; copying a MapsId pattern from a simple-id example; parent/child tables sharing a composite natural key; teams deriving FK primary keys per JPA 2.4.1 without matching the embeddable shape.
Related errors
- Identifier attribute '%s' of entity '%s' has type '%s' but i
- Attribute '%s' of entity '%s' is mapped by association '%s'
- Foreign key ({}:{} [{}])) must have same number of columns a
- Identity generation requires exactly one column
- Not implemented yet
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/c86d0bdbc6116afd.
Report an issue: GitHub.