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

  1. 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.
  2. 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.
  3. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/af24e4f570619f31. Report an issue: GitHub.