hibernate/hibernate-orm · error · AnnotationException
Collection '{}' is annotated both '@MapKey' and '@MapKeyColu
Error message
Collection '{}' is annotated both '@MapKey' and '@MapKeyColumn' What it means
A Map-valued collection must define exactly one key-mapping strategy: @MapKey (key is a property of the target entity or the target's primary key) or @MapKeyColumn (key is an explicit column of the collection table). When hasMapKeyProperty is already true and @MapKeyColumn is also present, checkMapKeyColumn throws this AnnotationException.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java:1144
collection.setInverse( isUnowned );
//TODO reduce tableBinder != null and oneToMany
scheduleSecondPass( isUnowned );
getMetadataCollector().addCollectionBinding( collection );
bindProperty();
}
private boolean isUnownedCollection() {
return mappedBy != null;
}
private boolean isMutable() {
return !property.hasDirectAnnotationUsage( Immutable.class );
}
private void checkMapKeyColumn() {
if ( property.hasDirectAnnotationUsage( MapKeyColumn.class ) && hasMapKeyProperty ) {
throw new AnnotationException( "Collection '" + qualify( propertyHolder.getPath(), propertyName )
+ "' is annotated both '@MapKey' and '@MapKeyColumn'" );
}
}
private void scheduleSecondPass(boolean isMappedBy) {
final var metadataCollector = getMetadataCollector();
//many to many may need some second pass information
if ( !oneToMany && isMappedBy ) {
metadataCollector.addMappedBy( getElementType().getName(), mappedBy, propertyName );
}
if ( inheritanceStatePerClass == null) {
throw new AssertionFailure( "inheritanceStatePerClass not set" );
}
metadataCollector.addSecondPass( getSecondPass(), !isMappedBy );
}
private void bindOptimisticLock(boolean isMappedBy) {View on GitHub (pinned to fad1729dce)
Solutions
- If the key is a column in the collection table, keep only @MapKeyColumn
- If the key is a target-entity property, keep only @MapKey(name = "...")
- For element-collection maps, also consider @MapKeyJoinColumn for entity-typed keys instead of stacking annotations
Example fix
// before @ManyToMany @MapKey(name = "isbn") // key = property of target @MapKeyColumn(name = "key_col") // error: conflicts with @MapKey Map<String, Book> books; // after @ManyToMany @MapKeyColumn(name = "key_col") // choose ONE key mapping strategy Map<String, Book> books;
Defensive patterns
Strategy: validation
Validate before calling
// Reject @MapKey together with @MapKeyColumn
static void checkMapKeyConflicts(Class<?>... entities) {
for ( Class<?> c : entities ) {
for ( Field f : c.getDeclaredFields() ) {
if ( f.isAnnotationPresent( MapKey.class )
&& f.isAnnotationPresent( MapKeyColumn.class ) ) {
throw new IllegalStateException( "Both @MapKey and @MapKeyColumn on "
+ c.getName() + "." + f.getName() );
}
}
}
} Prevention
- Pick one map-key strategy per map: @MapKey (property/PK), @MapKeyColumn (column), or @MapKeyJoinColumn (entity key)
- When changing key strategy, delete the old key annotation in the same edit
- Cover every map-valued collection in the bootstrap smoke test
When it happens
Trigger: A map collection property carries both @MapKey (or otherwise gets a map-key property bound) and @MapKeyColumn directly on the property; the check runs during bind() via checkMapKeyColumn().
Common situations: Copy-paste while switching key strategies from property-based to column-based; adding @MapKeyColumn to fix a key-column name without removing an older @MapKey; IDE suggesting both annotations for map mappings.
Related errors
- Error processing @TypeBinderType annotation '%s' for embedda
- Error processing @TypeBinderType annotation '%s' for entity
- error processing @AttributeBinderType annotation '%s' for at
- Wrong kind of binder for annotation type: '%s' does not acce
- @Convert placed on @Entity/@MappedSuperclass must define att
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/64225a2768ecaea5.
Report an issue: GitHub.