hibernate/hibernate-orm · error · AnnotationException
Identifier attribute '%s' of entity '%s' has type '%s' but i
Error message
Identifier attribute '%s' of entity '%s' has type '%s' but is mapped by association '%s' to entity '%s' with composite identifier type '%s'
What it means
Thrown when @MapsId references an entity with a composite @EmbeddedId and the dependent attribute is @EmbeddedId, but its Java type does not match the referenced entity's composite id class. checkMappedId compares property.getType().getName() with compositeId.getComponentClassName() via hasCompatibleType and rejects mismatches, printing expected vs actual type names.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java:1525
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(),
referencedEntityName,
expectedTypeName
)
);
}
}
else {
if ( !isSimpleId( property ) ) {
throw new AnnotationException(
"Attribute '%s' of entity '%s' is mapped by association '%s' but is not annotated '@Id'"
.formatted(
propertyName,View on GitHub (pinned to fad1729dce)
Solutions
- Reuse the parent's embeddable id class as the child's @EmbeddedId type (or a compatible identical type per hasCompatibleType).
- If the child truly needs extra key columns, put them in an embeddable that embeds the parent id type as a nested field and keep the mapped attribute's type aligned.
- Alternatively abandon @MapsId and map the FK columns explicitly with @JoinColumn plus a separate id.
Example fix
// before
@Embeddable
public class ChildId implements Serializable {
private Long parentId; // duplicate of ParentId - type mismatch
private String extra;
}
@EmbeddedId
private ChildId id;
// after: reuse the parent's id type
@Embeddable
public class ChildId implements Serializable {
@Embedded
private ParentId parentId; // same type as parent's @EmbeddedId
private String extra;
}
@EmbeddedId
private ChildId id;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("parentId")
private Parent parent; Defensive patterns
Strategy: validation
Validate before calling
// assert child EmbeddedId type equals parent EmbeddedId type before bootstrap
Class<?> parentIdType = parentIdEmbeddable(Parent.class); // e.g. ParentId
Class<?> childIdType = childEmbeddedIdType(Child.class);
if (!parentIdType.equals(childIdType)) {
throw new IllegalStateException("Child id type " + childIdType.getName()
+ " must match parent's " + parentIdType.getName());
} Try / catch
try {
metadata = sources.buildMetadata();
} catch (AnnotationException e) {
failBuild("MapsId type mismatch (expected vs actual in message): " + e.getMessage());
} Prevention
- Use the parent's id class directly as the child's EmbeddedId type.
- Add a unit test comparing both id classes for every @MapsId pair.
- Regenerate id classes from one shared source when code-generating.
When it happens
Trigger: Parent uses @EmbeddedId ParentId; child declares @EmbeddedId with a hand-written ChildId class that duplicates the fields instead of reusing the parent's id class (or uses an unrelated embeddable). Fires only when the parent binding is registered and its identifier is a Component.
Common situations: Developers writing a 'local' embeddable for the child's composite key instead of reusing the parent's id class; refactoring the parent's id class to a new name while the child keeps the old one; code-generated id classes diverging between modules.
Related errors
- Attribute '%s' of entity '%s' is mapped by association '%s'
- Attribute '%s' of entity '%s' is mapped by association '%s'
- Identifier field '{}' named in '@MapsId' does not exist in e
- Identifier property '" + getPath( holder, data ) + "' cannot
- Ambiguous persistent property methods declared by '%s': '%s'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/29e001d4deb4745f.
Report an issue: GitHub.