{"record":{"id":"92f405779d1388d7","repo":"hibernate/hibernate-orm","slug":"collection-has-foreign-key-in-secondary-table","errorCode":null,"errorMessage":"Collection '{}' has foreign key in secondary table","messagePattern":"Collection '(.+?)' has foreign key in secondary table","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java","lineNumber":506,"sourceCode":"\t\t\tPropertyHolder propertyHolder,\n\t\t\tPropertyData inferredData,\n\t\t\tMetadataBuildingContext context,\n\t\t\tMemberDetails property,\n\t\t\tAnnotatedJoinColumns joinColumns,\n\t\t\tOneToMany oneToManyAnn,\n\t\t\tManyToMany manyToManyAnn,\n\t\t\tElementCollection elementCollectionAnn,\n\t\t\tCollectionBinder collectionBinder) {\n\n\t\t//TODO enhance exception with @ManyToAny and @CollectionOfElements\n\t\tif ( oneToManyAnn != null && manyToManyAnn != null ) {\n\t\t\tthrow new AnnotationException( \"Property '\" + getPath( propertyHolder, inferredData )\n\t\t\t\t\t+ \"' is annotated both '@OneToMany' and '@ManyToMany'\" );\n\t\t}\n\t\tfinal String mappedBy;\n\t\tif ( oneToManyAnn != null ) {\n\t\t\tif ( joinColumns.isSecondary() ) {\n\t\t\t\tthrow new AnnotationException( \"Collection '\" + getPath( propertyHolder, inferredData )\n\t\t\t\t\t\t+ \"' has foreign key in secondary table\" );\n\t\t\t}\n\t\t\tcollectionBinder.setFkJoinColumns( joinColumns );\n\t\t\tmappedBy = nullIfEmpty( oneToManyAnn.mappedBy() );\n\t\t\tcollectionBinder.setTargetEntity( oneToManyAnn.targetEntity() );\n\t\t\tcollectionBinder.setCascadeStrategy(\n\t\t\t\t\taggregateCascadeTypes( oneToManyAnn.cascade(), property,\n\t\t\t\t\t\t\toneToManyAnn.orphanRemoval(), context ) );\n\t\t\tcollectionBinder.setOrphanRemoval( oneToManyAnn.orphanRemoval() );\n\t\t\tcollectionBinder.setOneToMany( true );\n\t\t}\n\t\telse if ( elementCollectionAnn != null ) {\n\t\t\tif ( joinColumns.isSecondary() ) {\n\t\t\t\tthrow new AnnotationException( \"Collection '\" + getPath( propertyHolder, inferredData )\n\t\t\t\t\t\t+ \"' has foreign key in secondary table\" );\n\t\t\t}\n\t\t\tcollectionBinder.setFkJoinColumns( joinColumns );\n\t\t\tmappedBy = null;","sourceCodeStart":488,"sourceCodeEnd":524,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java#L488-L524","documentation":"A @OneToMany collection's foreign-key join columns must live in a primary table, not in a secondary table of the owning entity. When the resolved join columns report isSecondary(), CollectionBinder throws this AnnotationException, because collection FK maintenance against a secondary table is not a supported mapping.","triggerScenarios":"The entity has @SecondaryTable mapping and the collection's @JoinColumn resolves to a column of that secondary table (joinColumns.isSecondary() true) while oneToManyAnn != null.","commonSituations":"An entity split across tables with @SecondaryTable where a child collection's FK column was placed in the secondary table; column-name collisions causing resolution into the secondary table; refactoring a single-table entity into primary + secondary without moving collection mappings.","solutions":["Point the collection's @JoinColumn at a column in the owning entity's primary table","Or drop the secondary-table mapping for that column and keep the FK in the primary table","Consider mapping the collection from a separate entity whose own table owns the FK"],"exampleFix":"// before\n@Entity\n@SecondaryTable(name = \"order_details\",\n    pkJoinColumns = @PrimaryKeyJoinColumn(name = \"order_id\"))\npublic class Order {\n    @OneToMany\n    @JoinColumn(name = \"detail_order_id\")   // resolves into order_details -> error\n    List<OrderLine> lines;\n}\n\n// after\npublic class Order {\n    @OneToMany(mappedBy = \"order\")   // FK lives in the child table (OrderLine.order)\n    List<OrderLine> lines;\n}","handlingStrategy":"validation","validationCode":"// Smoke-test: build the SessionFactory once in CI; secondary-table FK errors fail the build\npublic class MappingSmokeTest {\n    @Test\n    void allMappingsBoot() {\n        StandardServiceRegistry registry = new StandardServiceRegistryBuilder().build();\n        Metadata metadata = new MetadataSources( registry )\n            .addAnnotatedClass( Order.class )\n            .addAnnotatedClass( OrderLine.class )\n            .buildMetadata();\n        try ( SessionFactory sf = metadata.getSessionFactoryBuilder().build() ) {\n            // AnnotationException surfaces here instead of at production boot\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep collection FK join columns in the primary table; never point @OneToMany at secondary-table columns","When introducing @SecondaryTable, re-run the bootstrap test for every collection on that entity","Give secondary-table columns distinct names from primary-table columns to avoid wrong resolution"],"tags":["hibernate","jpa","one-to-many","secondary-table","join-column","foreign-key"],"backgroundTag":"foreign-key-in-secondary-table","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}