hibernate/hibernate-orm · error · MappingException

Foreign key ({}:{} [{}])) must have same number of columns a

Error message

Foreign key ({}:{} [{}])) must have same number of columns as the referenced primary key ({} [{}])

What it means

ForeignKey#alignColumns validates that a foreign key referencing the target primary key has exactly as many columns as that PK. A mismatch means the composite join does not line up with the referenced composite key, so Hibernate aborts before generating a broken FK, while also using the check to align column length/scale/precision.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/mapping/ForeignKey.java:88

			}
		}
	}

	public void setReferencedTable(Table referencedTable) throws MappingException {
		this.referencedTable = referencedTable;
	}

	/**
	 * Validates that column span of the foreign key and the primary key is the same.
	 * <p>
	 * Furthermore it aligns the length of the underlying tables columns.
	 */
	public void alignColumns() {
		if ( isReferenceToPrimaryKey() ) {
			final int columnSpan = getColumnSpan();
			final var primaryKey = referencedTable.getPrimaryKey();
			if ( primaryKey.getColumnSpan() != columnSpan ) {
				throw new MappingException( unalignedColumnsMessage( primaryKey ) );
			}

			//TODO: shouldn't this happen even for non-PK references?
			for ( int i = 0; i<columnSpan; i++ ) {
				final var referencedColumn = primaryKey.getColumn(i);
				final var referencingColumn = getColumn(i);
				referencingColumn.setLength( referencedColumn.getLength() );
				referencingColumn.setScale( referencedColumn.getScale() );
				referencingColumn.setPrecision( referencedColumn.getPrecision() );
				referencingColumn.setArrayLength( referencedColumn.getArrayLength() );
			}
		}
	}

	private String unalignedColumnsMessage(PrimaryKey primaryKey) {
		final var message = new StringBuilder();
		message.append( "Foreign key (" ).append( getName() ).append( ":" )
				.append( getTable().getName() )

View on GitHub (pinned to fad1729dce)

Solutions

  1. Declare one @JoinColumn per referenced PK column, each with an explicit referencedColumnName matching the PK column names.
  2. If the parent PK changed, update every child mapping's join columns in the same change.
  3. Check for accidentally duplicated or missing @JoinColumn annotations and align their order with the PK.

Example fix

// before - parent PK is composite (a, b)
@Id
@ManyToOne
@JoinColumn(name = "a")
private Parent parent;

// after
@Id
@ManyToOne
@JoinColumns({
    @JoinColumn(name = "a", referencedColumnName = "a"),
    @JoinColumn(name = "b", referencedColumnName = "b")
})
private Parent parent;
Defensive patterns

Strategy: try-catch

Try / catch

try {
    metadata = sources.buildMetadata();
} catch (MappingException e) {
    // message prints both key column lists - diff them to find the
    // missing or extra join column on the referencing side
    throw e;
}

Prevention

When it happens

Trigger: The target has a composite @EmbeddedId while the referencing side declares fewer @JoinColumn entries; the parent PK gained a column but child mappings were not updated; @JoinColumn entries referencing only part of the composite PK; column ordering changed inside @Embeddable.

Common situations: Evolving composite keys; hand-written @JoinColumn(name=...) without referencedColumnName; switching between @IdClass and @EmbeddedId; database refactors adding PK columns.

Related errors


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