hibernate/hibernate-orm · error · AnnotationException
Property '%s.%s' is not a collection and may not be a '@OneT
Error message
Property '%s.%s' is not a collection and may not be a '@OneToMany', '@ManyToMany', or '@ElementCollection'
What it means
The collection annotations @OneToMany, @ManyToMany, and @ElementCollection require a plural attribute. When determineSemanticJavaType is asked for the collection Java type of a non-plural property (property.isPlural() is false), Hibernate throws this AnnotationException naming the property and its declaring class.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java:1041
return CollectionClassification.MAP;
}
if ( java.util.Collection.class.isAssignableFrom( semanticJavaType ) ) {
return property.hasDirectAnnotationUsage( CollectionId.class )
? CollectionClassification.ID_BAG
: CollectionClassification.BAG;
}
return null;
}
private static Class<?> determineSemanticJavaType(MemberDetails property) {
if ( property.isPlural() ) {
return inferCollectionClassFromSubclass(
property.getType().determineRawClass().toJavaClass() );
}
else {
throw new AnnotationException(
String.format(
Locale.ROOT,
"Property '%s.%s' is not a collection and may not be a '@OneToMany', '@ManyToMany', or '@ElementCollection'",
property.getDeclaringType().getName(),
property.resolveAttributeName()
)
);
}
}
private static Class<?> inferCollectionClassFromSubclass(Class<?> clazz) {
for ( var priorityClass : INFERRED_CLASS_PRIORITY ) {
if ( priorityClass.isAssignableFrom( clazz ) ) {
return priorityClass;
}
}
return null;
}View on GitHub (pinned to fad1729dce)
Solutions
- For a single reference, use @ManyToOne (or @OneToOne) with @JoinColumn instead of a collection annotation
- If a collection is intended, declare the field as a Collection/List/Set/Map type
- In bidirectional relations, put the collection annotation (@OneToMany mappedBy=...) on the many-valued side and @ManyToOne on the single-valued side
Example fix
// before
@Entity
class Book {
@OneToMany(mappedBy = "book") // error: single reference, not a collection
Author author;
}
// after
@Entity
class Book {
@ManyToOne
@JoinColumn(name = "author_id")
Author author;
} Defensive patterns
Strategy: validation
Validate before calling
// Collection annotations require a plural field type
static void checkPluralFields(Class<?>... entities) {
for ( Class<?> c : entities ) {
for ( Field f : c.getDeclaredFields() ) {
boolean collectionAnn = f.isAnnotationPresent( OneToMany.class )
|| f.isAnnotationPresent( ManyToMany.class )
|| f.isAnnotationPresent( ElementCollection.class );
boolean plural = Collection.class.isAssignableFrom( f.getType() )
|| Map.class.isAssignableFrom( f.getType() )
|| f.getType().isArray();
if ( collectionAnn && !plural ) {
throw new IllegalStateException( "Collection annotation on non-collection field "
+ c.getName() + "." + f.getName() );
}
}
}
} Prevention
- Map single references with @ManyToOne/@OneToOne - the collection annotations are only for plural attributes
- In bidirectional pairs, the @OneToMany(mappedBy=...) side is always the collection field
- Add the plural-type scan to mapping tests to catch copy-paste mistakes early
When it happens
Trigger: A scalar field (single object reference, primitive, or wrapper) is annotated with @OneToMany, @ManyToMany, or @ElementCollection, so determineSemanticJavaType hits the else branch during classification.
Common situations: Using @OneToMany on a single back-reference that should be @ManyToOne; copy-paste of collection mappings onto single-valued fields; generics stripped during refactor so a helper exposes a single type; misunderstanding which side carries the collection annotation in bidirectional relationships.
Related errors
- Collection '{}' annotated '@NotFound' is not a '@ManyToMany'
- @SoftDelete cannot be applied to @OneToMany - {}.{}
- Property '{}' is annotated both '@OneToMany' and '@ManyToMan
- Error processing @TypeBinderType annotation '%s' for embedda
- Error processing @TypeBinderType annotation '%s' for entity
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/c3d734626e953184.
Report an issue: GitHub.