hibernate/hibernate-orm · error · MappingException

wrong number of generated columns

Error message

wrong number of generated columns

What it means

When an INSERT includes columns generated on execution, Hibernate asks the OnExecutionGenerator whether it references columns in SQL (referenceColumnsInSql(dialect)); if true, getReferencedColumnValues(dialect) must return exactly one value per generated column. A length mismatch between the column-name array and the value array throws MappingException("wrong number of generated columns") — the generator's contract is broken.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/sql/Insert.java:108

	public Insert addColumn(String columnName, String valueExpression) {
		columns.put( columnName, valueExpression );
		return this;
	}

	public Insert addIdentityColumn(String columnName) {
		final IdentityColumnSupport identityColumnSupport = dialect.getIdentityColumnSupport();
		if ( identityColumnSupport.hasIdentityInsertKeyword() ) {
			addColumn( columnName, identityColumnSupport.getIdentityInsertString() );
		}
		return this;
	}

	public Insert addGeneratedColumns(String[] columnNames, OnExecutionGenerator generator) {
		if ( generator.referenceColumnsInSql( dialect ) ) {
			String[] columnValues = generator.getReferencedColumnValues( dialect );
			if ( columnNames.length != columnValues.length ) {
				throw new MappingException("wrong number of generated columns"); //TODO!
			}
			for ( int i = 0; i < columnNames.length; i++ ) {
				addColumn( columnNames[i], columnValues[i] );
			}
		}
		return this;
	}

	public Insert setTableName(String tableName) {
		this.tableName = tableName;
		return this;
	}

	public String toStatementString() {
		final StringBuilder buf = new StringBuilder( columns.size()*15 + tableName.length() + 10 );

		if ( comment != null ) {
			buf.append( "/* " ).append( Dialect.escapeComment( comment ) ).append( " */ " );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Return an array whose length equals the number of generated columns passed in — one value per column
  2. If the database generates the values itself (DEFAULT/identity), return referenceColumnsInSql() == false and an empty array instead
  3. Unit-test the generator against every dialect it can run under, asserting columnNames.length == columnValues.length

Example fix

// before
public class TriggerDefaultGenerator implements OnExecutionGenerator {
    public boolean referenceColumnsInSql(Dialect d) { return true; }
    public String[] getReferencedColumnValues(Dialect d) {
        return new String[] { "default" }; // entity has TWO generated columns -> mismatch
    }
}

// after
public boolean referenceColumnsInSql(Dialect d) { return false; } // DB generates values
public String[] getReferencedColumnValues(Dialect d) { return ArrayHelper.EMPTY_STRING_ARRAY; }
Defensive patterns

Strategy: validation

Validate before calling

// put this in the generator itself or its unit test
assert columnNames.length == getReferencedColumnValues(dialect).length
        || !referenceColumnsInSql(dialect) : "generated column/value count mismatch";

Prevention

When it happens

Trigger: A custom OnExecutionGenerator (used with @Generated / generated-on-insert mappings) whose getReferencedColumnValues returns a wrong-length array — e.g., returning a single default keyword for an entity with two generated columns; dialect-conditional arrays that forget a dialect branch.

Common situations: Writing custom generators for DB defaults, trigger-populated columns, or tenant placeholders; porting generators from the old GeneratedValue/BeforeExecutionGenerator contracts where the array rules differ.

Related errors


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