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

  1. Keep exactly one of @OneToMany / @ManyToMany on the property and delete the other
  2. If the design genuinely needs both shapes, split into two distinct properties with separate mappings
  3. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/0f8b1d034f05f8b3. Report an issue: GitHub.