hibernate/hibernate-orm · error · UnknownParameterException

Unable to locate parameter `%s.%s` for %s - %s : %s

Error message

Unable to locate parameter `%s.%s` for %s - %s : %s

What it means

MutationOperation.getJdbcValueDescriptor(columnName, usage) is the throwing variant of findValueDescriptor: it locates the JdbcValueDescriptor (JDBC position and type) for a column under a specific ParameterUsage (SET, WHERE, VERSION, RETURNING) of an insert/update/delete operation. If no descriptor matches, it throws UnknownParameterException carrying mutation type, target, table, column and usage - meaning the requested column/usage combination is not part of that operation's SQL.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/sql/model/MutationOperation.java:99

	TableMapping getTableDetails();

	/**
	 * Find the JDBC parameter to be used for the specified column.
	 *
	 * @return The descriptor, or null if none match.
	 *
	 * @see #getJdbcValueDescriptor
	 */
	JdbcValueDescriptor findValueDescriptor(String columnName, ParameterUsage usage);

	/**
	 * Form of {@link #findValueDescriptor}, throwing an exception if not found as opposed
	 * to simply returning null
	 */
	default JdbcValueDescriptor getJdbcValueDescriptor(String columnName, ParameterUsage usage) {
		final JdbcValueDescriptor parameterDescriptor = findValueDescriptor( columnName, usage );
		if ( parameterDescriptor == null ) {
			throw new UnknownParameterException( getMutationType(), getMutationTarget(), getTableDetails().getTableName(), columnName, usage );
		}
		return parameterDescriptor;
	}

	@Override
	default String resolvePhysicalTableName(String tableName) {
		assert getTableDetails().getTableName().equals( tableName );
		return tableName;
	}

	@Override
	default JdbcValueDescriptor resolveValueDescriptor(String tableName, String columnName, ParameterUsage usage) {
		assert getTableDetails().getTableName().equals( tableName );
		return findValueDescriptor( columnName, usage );
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Align custom SQL with the mapping: every participating mapped column needs its placeholder in the @SQL* statement
  2. Check the requested usage - VERSION parameters exist only on versioned mutations, RETURNING only where supported
  3. Call findValueDescriptor(...) and handle null when the column may legitimately be absent, instead of getJdbcValueDescriptor(...)
  4. Regenerate/verify custom DML after adding, removing or reordering entity columns

Example fix

// before
@SQLDelete( sql = "UPDATE Company SET deleted = true WHERE id = ?" ) // missing version predicate

// after
@SQLDelete( sql = "UPDATE Company SET deleted = true WHERE id = ? AND version = ?" )
Defensive patterns

Strategy: validation

Validate before calling

JdbcValueDescriptor d = operation.findValueDescriptor( columnName, usage );
if ( d == null ) {
    // column not part of this mutation's SQL: skip or rebuild the operation
} else { bind( d ); }

Prevention

When it happens

Trigger: Binding a mutation built from custom SQL (@SQLInsert/@SQLUpdate/@SQLDelete) that lacks a placeholder for a column; requesting ParameterUsage.VERSION on a non-versioned operation; custom MutationOperation/MutationExecutor implementations resolving descriptors by column name for columns outside the mutated table; secondary-table columns resolved against the primary-table mutation.

Common situations: Custom DML annotations with fewer '?' placeholders than mapped columns require; schema/mapping drift after adding columns without regenerating custom SQL; custom flush pipelines; upgrades that reclassify parameter usage.

Related errors


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