{"record":{"id":"7645952d2942deab","repo":"hibernate/hibernate-orm","slug":"softdelete-cannot-be-applied-to-onetomany","errorCode":null,"errorMessage":"@SoftDelete cannot be applied to @OneToMany - {}.{}","messagePattern":"@SoftDelete cannot be applied to @OneToMany - (.+?)\\.(.+?)","errorType":"exception","errorClass":"UnsupportedMappingException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java","lineNumber":440,"sourceCode":"\t\t\t\t: new WrappedInferredData(inferredData, \"element\" );\n\t}\n\n\tprivate static void checkAnnotations(\n\t\t\tPropertyHolder propertyHolder,\n\t\t\tPropertyData inferredData,\n\t\t\tMemberDetails property,\n\t\t\tOneToMany oneToMany,\n\t\t\tManyToMany manyToMany,\n\t\t\tElementCollection elementCollection) {\n\t\tif ( ( oneToMany != null || manyToMany != null || elementCollection != null )\n\t\t\t\t&& isToManyAssociationWithinEmbeddableCollection( propertyHolder ) ) {\n\t\t\tthrow new AnnotationException( \"Property '\" + getPath( propertyHolder, inferredData ) +\n\t\t\t\t\t\"' belongs to an '@Embeddable' class that is contained in an '@ElementCollection' and may not be a \"\n\t\t\t\t\t+ annotationName( oneToMany, manyToMany, elementCollection ));\n\t\t}\n\n\t\tif ( oneToMany != null && property.hasDirectAnnotationUsage( SoftDelete.class ) ) {\n\t\t\tthrow new UnsupportedMappingException(\n\t\t\t\t\t\"@SoftDelete cannot be applied to @OneToMany - \" +\n\t\t\t\t\t\t\tproperty.getDeclaringType().getName() + \".\" + property.getName()\n\t\t\t);\n\t\t}\n\n\t\tif ( property.hasDirectAnnotationUsage( OrderColumn.class )\n\t\t\t\t&& manyToMany != null\n\t\t\t\t&& isNotBlank( manyToMany.mappedBy() ) ) {\n\t\t\tthrow new AnnotationException(\"Collection '\" + getPath( propertyHolder, inferredData ) +\n\t\t\t\t\t\"' is the unowned side of a bidirectional '@ManyToMany' and may not have an '@OrderColumn'\");\n\t\t}\n\n\t\tif ( manyToMany != null || elementCollection != null ) {\n\t\t\tif ( property.hasDirectAnnotationUsage( JoinColumn.class )\n\t\t\t\t\t|| property.hasDirectAnnotationUsage( JoinColumns.class ) ) {\n\t\t\t\tthrow new AnnotationException( \"Property '\" + getPath( propertyHolder, inferredData )\n\t\t\t\t\t\t+ \"' is a \" + annotationName( oneToMany, manyToMany, elementCollection )\n\t\t\t\t\t\t+ \" and is directly annotated '@JoinColumn'\"","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java#L422-L458","documentation":"@SoftDelete marks an entity type as soft-deletable; it belongs on the entity class (the target of deletion), never on a @OneToMany collection property. CollectionBinder.checkAnnotations throws this UnsupportedMappingException when a one-to-many property directly carries @SoftDelete.","triggerScenarios":"A property with @OneToMany also has a direct @SoftDelete annotation (property.hasDirectAnnotationUsage(SoftDelete.class) is true during checkAnnotations).","commonSituations":"Adopting Hibernate's soft-delete feature and assuming the annotation goes where deletion is observed (the collection) rather than on the deleted entity; upgrading codebases that used custom soft-delete solutions with collection-level markers.","solutions":["Move @SoftDelete from the @OneToMany property to the target entity class","If several entities participate, annotate each soft-deletable entity class with @SoftDelete","Keep the @OneToMany side free of soft-delete annotations; filtering is derived from the target entity's soft-delete setup"],"exampleFix":"// before\n@Entity\nclass Parent {\n    @OneToMany(mappedBy = \"parent\")\n    @SoftDelete                  // error: belongs on the child entity\n    List<Child> children;\n}\n\n// after\n@Entity\n@SoftDelete\npublic class Child {\n    @ManyToOne\n    Parent parent;\n}\n\n@Entity\nclass Parent {\n    @OneToMany(mappedBy = \"parent\")\n    List<Child> children;\n}","handlingStrategy":"validation","validationCode":"// Reject @SoftDelete placed on @OneToMany properties before boot\nstatic void checkSoftDeletePlacement(Class<?>... entities) {\n    for ( Class<?> c : entities ) {\n        for ( Field f : c.getDeclaredFields() ) {\n            if ( f.isAnnotationPresent( SoftDelete.class )\n                    && f.isAnnotationPresent( OneToMany.class ) ) {\n                throw new IllegalStateException( \"@SoftDelete on @OneToMany property: \"\n                    + c.getName() + \".\" + f.getName() );\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember @SoftDelete is a type-level concern: put it on entity classes only","During soft-delete adoption, add an architecture test that forbids @SoftDelete on fields","Run the bootstrap smoke test so placement errors fail CI, not production start"],"tags":["hibernate","jpa","soft-delete","one-to-many","annotation-binding"],"backgroundTag":"soft-delete-misplacement","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}