{"record":{"id":"1548c2cb5f47a0d9","repo":"hibernate/hibernate-orm","slug":"collection-annotated-notfound-is-not-a-m","errorCode":null,"errorMessage":"Collection '{}' annotated '@NotFound' is not a '@ManyToMany' association","messagePattern":"Collection '(.+?)' annotated '@NotFound' is not a '@ManyToMany' association","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java","lineNumber":386,"sourceCode":"\tprivate static void addDefinition(\n\t\t\tIdentifierGeneratorDefinition definition,\n\t\t\tMap<String, IdentifierGeneratorDefinition> availableGenerators) {\n\t\tfinal String definitionName = definition.getName();\n\t\tif ( !definitionName.isEmpty() ) {\n\t\t\tavailableGenerators.put( definitionName, definition );\n\t\t}\n\t}\n\n\tprivate static NotFoundAction notFoundAction(\n\t\t\tPropertyHolder propertyHolder,\n\t\t\tPropertyData inferredData,\n\t\t\tMemberDetails property,\n\t\t\tManyToMany manyToManyAnn,\n\t\t\tModelsContext sourceModelContext) {\n\t\tfinal var notFound = property.getAnnotationUsage( NotFound.class, sourceModelContext );\n\t\tif ( notFound != null ) {\n\t\t\tif ( manyToManyAnn == null ) {\n\t\t\t\tthrow new AnnotationException( \"Collection '\" + getPath(propertyHolder, inferredData)\n\t\t\t\t\t\t+ \"' annotated '@NotFound' is not a '@ManyToMany' association\" );\n\t\t\t}\n\t\t\treturn notFound.action();\n\t\t}\n\t\telse {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static AnnotatedJoinColumns mapKeyJoinColumns(\n\t\t\tPropertyHolder propertyHolder,\n\t\t\tPropertyData inferredData,\n\t\t\tEntityBinder entityBinder,\n\t\t\tMetadataBuildingContext context,\n\t\t\tMemberDetails property) {\n\t\treturn buildJoinColumnsWithDefaultColumnSuffix(\n\t\t\t\tmapKeyJoinColumnAnnotations( property, context ),\n\t\t\t\tnull,","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java#L368-L404","documentation":"In this Hibernate version, @NotFound on a collection-valued association is only supported for @ManyToMany. CollectionBinder.notFoundAction throws this AnnotationException when @NotFound is present on a collection property but the @ManyToMany annotation is absent (e.g. a @OneToMany or an un-annotated collection).","triggerScenarios":"A collection property carries @NotFound(action = ...) but the manyToManyAnn parameter passed to notFoundAction(...) is null - i.e. the property is a @OneToMany or has no to-many annotation at all.","commonSituations":"Migrating from older Hibernate versions where @NotFound on @OneToMany was silently tolerated or handled; copying @ManyToOne/@NotFound patterns onto collections; expecting lazy not-found semantics on a one-to-many collection.","solutions":["Remove @NotFound from the collection property","If not-found tolerance is truly required, model the association as @ManyToMany and keep @NotFound there","For a @OneToMany, handle missing rows in application code or fix the data so referenced rows always exist"],"exampleFix":"// before\n@Entity\nclass PurchaseOrder {\n    @OneToMany(mappedBy = \"order\")\n    @NotFound(action = NotFoundAction.IGNORE)   // error: only @ManyToMany supports @NotFound\n    List<OrderLine> lines;\n}\n\n// after\n@Entity\nclass PurchaseOrder {\n    @OneToMany(mappedBy = \"order\")\n    List<OrderLine> lines;\n}","handlingStrategy":"validation","validationCode":"// Scan annotated classes before boot: @NotFound on a collection that is not @ManyToMany\nstatic void checkNotFoundOnlyOnManyToMany(Class<?>... entities) {\n    for ( Class<?> c : entities ) {\n        for ( Field f : c.getDeclaredFields() ) {\n            if ( f.isAnnotationPresent( NotFound.class )\n                    && !f.isAnnotationPresent( ManyToMany.class ) ) {\n                throw new IllegalStateException( \"@NotFound on non-@ManyToMany collection: \"\n                    + c.getName() + \".\" + f.getName() );\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Reserve @NotFound for @ManyToOne/@OneToOne and @ManyToMany - never @OneToMany collections","When upgrading Hibernate, grep the codebase for @NotFound usages on collections and remove them","Fix referential integrity in data instead of relying on not-found tolerance for collections"],"tags":["hibernate","jpa","notfound","one-to-many","many-to-many","annotation-binding"],"backgroundTag":"notfound-annotation-misuse","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}