hibernate/hibernate-orm · error · AnnotationException
Collection '{}' has foreign key in secondary table
Error message
Collection '{}' has foreign key in secondary table What it means
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.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java:506
PropertyHolder propertyHolder,
PropertyData inferredData,
MetadataBuildingContext context,
MemberDetails property,
AnnotatedJoinColumns joinColumns,
OneToMany oneToManyAnn,
ManyToMany manyToManyAnn,
ElementCollection elementCollectionAnn,
CollectionBinder collectionBinder) {
//TODO enhance exception with @ManyToAny and @CollectionOfElements
if ( oneToManyAnn != null && manyToManyAnn != null ) {
throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData )
+ "' is annotated both '@OneToMany' and '@ManyToMany'" );
}
final String mappedBy;
if ( oneToManyAnn != null ) {
if ( joinColumns.isSecondary() ) {
throw new AnnotationException( "Collection '" + getPath( propertyHolder, inferredData )
+ "' has foreign key in secondary table" );
}
collectionBinder.setFkJoinColumns( joinColumns );
mappedBy = nullIfEmpty( oneToManyAnn.mappedBy() );
collectionBinder.setTargetEntity( oneToManyAnn.targetEntity() );
collectionBinder.setCascadeStrategy(
aggregateCascadeTypes( oneToManyAnn.cascade(), property,
oneToManyAnn.orphanRemoval(), context ) );
collectionBinder.setOrphanRemoval( oneToManyAnn.orphanRemoval() );
collectionBinder.setOneToMany( true );
}
else if ( elementCollectionAnn != null ) {
if ( joinColumns.isSecondary() ) {
throw new AnnotationException( "Collection '" + getPath( propertyHolder, inferredData )
+ "' has foreign key in secondary table" );
}
collectionBinder.setFkJoinColumns( joinColumns );
mappedBy = null;View on GitHub (pinned to fad1729dce)
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
Example fix
// before
@Entity
@SecondaryTable(name = "order_details",
pkJoinColumns = @PrimaryKeyJoinColumn(name = "order_id"))
public class Order {
@OneToMany
@JoinColumn(name = "detail_order_id") // resolves into order_details -> error
List<OrderLine> lines;
}
// after
public class Order {
@OneToMany(mappedBy = "order") // FK lives in the child table (OrderLine.order)
List<OrderLine> lines;
} Defensive patterns
Strategy: validation
Validate before calling
// Smoke-test: build the SessionFactory once in CI; secondary-table FK errors fail the build
public class MappingSmokeTest {
@Test
void allMappingsBoot() {
StandardServiceRegistry registry = new StandardServiceRegistryBuilder().build();
Metadata metadata = new MetadataSources( registry )
.addAnnotatedClass( Order.class )
.addAnnotatedClass( OrderLine.class )
.buildMetadata();
try ( SessionFactory sf = metadata.getSessionFactoryBuilder().build() ) {
// AnnotationException surfaces here instead of at production boot
}
}
} Prevention
- 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
When it happens
Trigger: The entity has @SecondaryTable mapping and the collection's @JoinColumn resolves to a column of that secondary table (joinColumns.isSecondary() true) while oneToManyAnn != null.
Common situations: 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.
Related errors
- Secondary table '${explicitTableName}' for property '${prope
- Column mappings for property '${propertyName}' mix distinct
- A '@JoinColumn' references a column named '{}' but the targe
- Collection '{}' annotated '@NotFound' is not a '@ManyToMany'
- @SoftDelete cannot be applied to @OneToMany - {}.{}
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/92f405779d1388d7.
Report an issue: GitHub.