hibernate/hibernate-orm · error · AnnotationException
Collection '{}' is the unowned side of a bidirectional '@Man
Error message
Collection '{}' is the unowned side of a bidirectional '@ManyToMany' and may not have an '@OrderColumn' What it means
On the unowned (mappedBy) side of a bidirectional @ManyToMany, the join and its ordering data live in the owning side's join table, so an @OrderColumn there has no column to map. CollectionBinder.checkAnnotations throws this AnnotationException when @OrderColumn is present and manyToMany.mappedBy() is non-blank.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java:449
ElementCollection elementCollection) {
if ( ( oneToMany != null || manyToMany != null || elementCollection != null )
&& isToManyAssociationWithinEmbeddableCollection( propertyHolder ) ) {
throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData ) +
"' belongs to an '@Embeddable' class that is contained in an '@ElementCollection' and may not be a "
+ annotationName( oneToMany, manyToMany, elementCollection ));
}
if ( oneToMany != null && property.hasDirectAnnotationUsage( SoftDelete.class ) ) {
throw new UnsupportedMappingException(
"@SoftDelete cannot be applied to @OneToMany - " +
property.getDeclaringType().getName() + "." + property.getName()
);
}
if ( property.hasDirectAnnotationUsage( OrderColumn.class )
&& manyToMany != null
&& isNotBlank( manyToMany.mappedBy() ) ) {
throw new AnnotationException("Collection '" + getPath( propertyHolder, inferredData ) +
"' is the unowned side of a bidirectional '@ManyToMany' and may not have an '@OrderColumn'");
}
if ( manyToMany != null || elementCollection != null ) {
if ( property.hasDirectAnnotationUsage( JoinColumn.class )
|| property.hasDirectAnnotationUsage( JoinColumns.class ) ) {
throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData )
+ "' is a " + annotationName( oneToMany, manyToMany, elementCollection )
+ " and is directly annotated '@JoinColumn'"
+ " (specify '@JoinColumn' inside '@JoinTable' or '@CollectionTable')" );
}
}
}
private static String annotationName(
OneToMany oneToMany,
ManyToMany manyToMany,
ElementCollection elementCollection) {View on GitHub (pinned to fad1729dce)
Solutions
- Remove @OrderColumn from the mappedBy side and keep it on the owning side
- Or drop mappedBy on this side and make it the owner with @JoinTable + @OrderColumn (then clean up the other side)
- If you need ordered iteration on the inverse side, sort in queries instead of mapping an index
Example fix
// before
@Entity
class Course {
@ManyToMany(mappedBy = "courses")
@OrderColumn // error: unowned side may not have @OrderColumn
List<Student> students;
}
// after
@Entity
class Course {
@ManyToMany(mappedBy = "courses")
List<Student> students; // ordering handled by the owning side
}
@Entity
class Student {
@ManyToMany
@JoinTable(name = "student_course")
@OrderColumn
List<Course> courses; // owner carries the order column
} Defensive patterns
Strategy: validation
Validate before calling
// Reject @OrderColumn on the mappedBy side of a @ManyToMany before boot
static void checkOrderColumnOwnership(Class<?>... entities) {
for ( Class<?> c : entities ) {
for ( Field f : c.getDeclaredFields() ) {
ManyToMany m2m = f.getAnnotation( ManyToMany.class );
if ( f.isAnnotationPresent( OrderColumn.class )
&& m2m != null && !m2m.mappedBy().isBlank() ) {
throw new IllegalStateException( "@OrderColumn on unowned side: "
+ c.getName() + "." + f.getName() );
}
}
}
} Prevention
- Keep join and ordering metadata exclusively on the owning side of bidirectional relations
- When adding @OrderColumn, search the mapping pair and edit only the side without mappedBy
- Order inverse-side iteration in queries (ORDER BY) rather than mapping an index
When it happens
Trigger: A property carries @ManyToMany(mappedBy = "...") together with @OrderColumn (directly or via hasDirectAnnotationUsage) during checkAnnotations.
Common situations: Adding @OrderColumn to both sides of a bidirectional many-to-many for consistent ordering; copy-pasting the owning side's annotations to the inverse side; migrating a unidirectional ordered many-to-many to bidirectional without cleaning up.
Related errors
- Collection '{}' annotated '@NotFound' is not a '@ManyToMany'
- Property '{}' is annotated both '@OneToMany' and '@ManyToMan
- Association '{}' is 'mappedBy' another entity and may not sp
- Property '${property}' uses *-to-many mapping with mappedBy
- Error processing @TypeBinderType annotation '%s' for embedda
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/597efbe6e08e6d02.
Report an issue: GitHub.