hibernate/hibernate-orm · error · AssertionFailure
Number of referencing columns [%s] did not match number of r
Error message
Number of referencing columns [%s] did not match number of referenced columns [%s] in foreign-key [%s] from [%s] to [%s]
What it means
When emitting foreign-key DDL for an FK that references the primary key of the target table, StandardForeignKeyExporter.getTargetColumns must pair every referencing column with a PK column. If the number of referencing columns differs from the referenced PK's column span, this AssertionFailure aborts DDL generation, reporting both counts, the FK name, and both table names. The mapping promises a different arity than the referenced key actually has.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/tool/schema/internal/StandardForeignKeyExporter.java:99
String[] targetColumnNames) {
final String keyDefinition = foreignKey.getKeyDefinition();
final String constraintName = quotedConstraintName( foreignKey, metadata );
return keyDefinition != null
? dialect.getAddForeignKeyConstraintString( constraintName, keyDefinition )
: dialect.getAddForeignKeyConstraintString( constraintName, columnNames,
targetTableName, targetColumnNames, foreignKey.isReferenceToPrimaryKey() );
}
private String quotedConstraintName(ForeignKey foreignKey, Metadata metadata) {
return metadata.getDatabase().getJdbcEnvironment().getIdentifierHelper()
.toIdentifier( foreignKey.getName() ).render( dialect );
}
private static List<Column> getTargetColumns(ForeignKey foreignKey, int numberOfColumns) {
if ( foreignKey.isReferenceToPrimaryKey() ) {
final var primaryKey = foreignKey.getReferencedTable().getPrimaryKey();
if ( numberOfColumns != primaryKey.getColumnSpan() ) {
throw new AssertionFailure(
String.format(
Locale.ENGLISH,
COLUMN_MISMATCH_MSG,
numberOfColumns,
primaryKey.getColumnSpan(),
foreignKey.getName(),
foreignKey.getTable().getName(),
foreignKey.getReferencedTable().getName()
)
);
}
return primaryKey.getColumns();
}
else {
final var referencedColumns = foreignKey.getReferencedColumns();
if ( numberOfColumns != referencedColumns.size() ) {
throw new AssertionFailure(
String.format(View on GitHub (pinned to fad1729dce)
Solutions
- Match arity: for a composite referenced PK provide one @JoinColumn per PK column (wrapped in @JoinColumns), each with an explicit referencedColumnName, in the same order as the id fields.
- If the FK intentionally references only part of the key, point referencedColumnName at a unique non-PK column so isReferenceToPrimaryKey() is false and the explicit referenced-columns branch is used.
- Verify the referenced id mapping itself: @EmbeddedId field order / @IdClass fields must line up with the @JoinColumns order.
Example fix
// before: OrderHeader has composite PK (orderId, line)
@ManyToOne
@JoinColumn(name = "order_id", referencedColumnName = "orderId")
private OrderHeader header;
// after: one entry per PK column, same order as the id mapping
@ManyToOne
@JoinColumns({
@JoinColumn(name = "order_id", referencedColumnName = "orderId"),
@JoinColumn(name = "order_line", referencedColumnName = "line")
})
private OrderHeader header; Defensive patterns
Strategy: validation
Validate before calling
// before export: assert join-column count equals the referenced composite PK span
int joinCount = property.isAnnotationPresent(JoinColumns.class)
? property.getAnnotation(JoinColumns.class).value().length
: (property.isAnnotationPresent(JoinColumn.class) ? 1 : 0);
int pkSpan = referencedEntity.getIdColumnCount(); // from id metadata / @EmbeddedId fields
if (joinCount != 0 && joinCount != pkSpan) {
throw new IllegalStateException("FK arity mismatch on " + property + ": " + joinCount + " vs PK span " + pkSpan);
} Try / catch
try {
new SchemaExport(metadata).createOnly(); // or exporter call
} catch (AssertionFailure af) {
if (af.getMessage() != null && af.getMessage().startsWith("Number of referencing columns")) {
// align @JoinColumns one-per-column with the referenced primary key, rebuild metadata, retry
} else { throw af; }
} Prevention
- Always specify referencedColumnName on every @JoinColumn that targets a composite key.
- Add a CI step that exports schema to STDOUT for all entities; arity bugs surface there before runtime.
- Re-audit association mappings whenever a primary key gains or loses a column.
When it happens
Trigger: An association whose referencing side has fewer or more columns than the referenced @EmbeddedId/@IdClass primary key: a single @JoinColumn pointing at a two-column composite PK; a @ManyToOne to a composite-key entity without @JoinColumns covering every key column; refactoring that added a column to the PK without updating the join mapping; @OneToMany with @JoinColumn(s) on the child that do not span the parent's composite id.
Common situations: Composite-key entities (@EmbeddedId/@IdClass) where developers map only the 'main' join column; legacy schemas whose FK covers only part of the PK; copy-pasted associations between entities with different key shapes; schema validation/export run in CI exposing previously untested mappings.
Related errors
- Foreign key ({}:{} [{}])) must have same number of columns a
- No drop foreign key syntax supported by SQLiteDialect
- No add foreign key syntax supported by SQLiteDialect
- SingleStore does not support foreign keys and referential in
- SingleStore does not support foreign keys and referential in
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/af24e4f570619f31.
Report an issue: GitHub.