hibernate/hibernate-orm · error · AnnotationException
Property '{}' is annotated both '@OneToMany' and '@ManyToMan
Error message
Property '{}' is annotated both '@OneToMany' and '@ManyToMany' What it means
A single property cannot be mapped as both @OneToMany and @ManyToMany; the two annotations define different relation shapes. CollectionBinder rejects the combination at the very start of collection binding, before any further processing happens.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java:500
entityBinder.getSecondaryTables(),
context
);
}
private static String handleTargetEntity(
PropertyHolder propertyHolder,
PropertyData inferredData,
MetadataBuildingContext context,
MemberDetails property,
AnnotatedJoinColumns joinColumns,
OneToMany oneToManyAnn,
ManyToMany manyToManyAnn,
ElementCollection elementCollectionAnn,
CollectionBinder collectionBinder) {
//TODO enhance exception with @ManyToAny and @CollectionOfElements
if ( oneToManyAnn != null && manyToManyAnn != null ) {
throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData )
+ "' is annotated both '@OneToMany' and '@ManyToMany'" );
}
final String mappedBy;
if ( oneToManyAnn != null ) {
if ( joinColumns.isSecondary() ) {
throw new AnnotationException( "Collection '" + getPath( propertyHolder, inferredData )
+ "' has foreign key in secondary table" );
}
collectionBinder.setFkJoinColumns( joinColumns );
mappedBy = nullIfEmpty( oneToManyAnn.mappedBy() );
collectionBinder.setTargetEntity( oneToManyAnn.targetEntity() );
collectionBinder.setCascadeStrategy(
aggregateCascadeTypes( oneToManyAnn.cascade(), property,
oneToManyAnn.orphanRemoval(), context ) );
collectionBinder.setOrphanRemoval( oneToManyAnn.orphanRemoval() );
collectionBinder.setOneToMany( true );
}
else if ( elementCollectionAnn != null ) {View on GitHub (pinned to fad1729dce)
Solutions
- Keep exactly one of @OneToMany / @ManyToMany on the property and delete the other
- If the design genuinely needs both shapes, split into two distinct properties with separate mappings
- Check for stale annotations from refactoring sessions, including on getters vs fields (mixed access can hide the duplicate)
Example fix
// before @OneToMany(mappedBy = "order") @ManyToMany // error: both present List<Tag> tags; // after @OneToMany(mappedBy = "order") List<Tag> tags;
Defensive patterns
Strategy: validation
Validate before calling
// Reject fields carrying both @OneToMany and @ManyToMany
static void checkSingleCollectionAnnotation(Class<?>... entities) {
for ( Class<?> c : entities ) {
for ( Field f : c.getDeclaredFields() ) {
if ( f.isAnnotationPresent( OneToMany.class )
&& f.isAnnotationPresent( ManyToMany.class ) ) {
throw new IllegalStateException( "Both @OneToMany and @ManyToMany on "
+ c.getName() + "." + f.getName() );
}
}
}
} Prevention
- After changing a relation's cardinality, delete the replaced annotation immediately
- Beware of annotations split across field and getter - check both access paths
- Use IDE annotation-usage search for @OneToMany and @ManyToMany overlaps during reviews
When it happens
Trigger: The same field or getter carries both @OneToMany and @ManyToMany, so oneToManyAnn != null && manyToManyAnn != null in the binding routine.
Common situations: IDE auto-complete or auto-import inserting the wrong annotation alongside the right one; refactoring a unidirectional one-to-many into a many-to-many and forgetting to delete the old annotation; copy-paste between properties.
Related errors
- Collection '{}' annotated '@NotFound' is not a '@ManyToMany'
- @SoftDelete cannot be applied to @OneToMany - {}.{}
- Collection '{}' is the unowned side of a bidirectional '@Man
- Property '%s.%s' is not a collection and may not be a '@OneT
- Property '${property}' uses *-to-many mapping with mappedBy
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/0f8b1d034f05f8b3.
Report an issue: GitHub.