hibernate/hibernate-orm · error · ExecutionException

JDBC parameter value not bound - %s

Error message

JDBC parameter value not bound - %s

What it means

When binding a PreparedStatement, Hibernate looks up the JdbcParameterBinding for each JdbcParameter in JdbcParameterBindings. If no binding was collected for that parameter instance during query translation/execution preparation, bindParameterValue throws ExecutionException('JDBC parameter value not bound - ' + parameter). The SQL contains a '?' whose parameter object has no entry in the binding map - a translation/binding pipeline mismatch, not a value the user forgot to set.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java:86

	public void accept(SqlAstWalker sqlTreeWalker) {
		sqlTreeWalker.visitParameter( this );
	}

	@Override
	public JdbcMapping getSingleJdbcMapping() {
		return jdbcMapping;
	}


	@Override
	public void bindParameterValue(
			PreparedStatement statement,
			int startPosition,
			JdbcParameterBindings jdbcParamBindings,
			ExecutionContext executionContext) throws SQLException {
		final var binding = jdbcParamBindings.getBinding( AbstractJdbcParameter.this );
		if ( binding == null ) {
			throw new ExecutionException( "JDBC parameter value not bound - " + this );
		}

		final var jdbcMapping = jdbcMapping( executionContext, binding );
		bindParameterValue( jdbcMapping, statement, binding.getBindValue(), startPosition, executionContext );
	}

	private JdbcMapping jdbcMapping(ExecutionContext executionContext, JdbcParameterBinding binding) {
		JdbcMapping jdbcMapping = binding.getBindType();

		if ( jdbcMapping == null ) {
			jdbcMapping = this.jdbcMapping;
		}

		// If the parameter type is not known from the context i.e. null or Object, infer it from the bind value
		if ( jdbcMapping == null || jdbcMapping.getMappedJavaType().getJavaTypeClass() == Object.class ) {
			jdbcMapping = guessBindType( executionContext, binding.getBindValue(), jdbcMapping );
		}

View on GitHub (pinned to fad1729dce)

Solutions

  1. If a custom function/dialect creates JdbcParameters, register matching JdbcParameterBindings during rendering (mirror how standard functions do it)
  2. Clear or size down the query plan cache (hibernate.query.plan_cache_max_size, or restart) after upgrading Hibernate or swapping dialects to rule out stale plans
  3. Reproduce with a minimal entity/query and check/report the Hibernate JIRA (HHH) - this message typically indicates an internal bug
  4. As a workaround, disable the plan cache (hibernate.query.plan_cache_max_size=0) to force retranslation per execution
Defensive patterns

Strategy: try-catch

Try / catch

try {
    return query.list();
} catch ( ExecutionException e ) {
    if ( e.getMessage() != null && e.getMessage().startsWith( "JDBC parameter value not bound" ) ) {
        // translation/binding mismatch: force retranslation, then retry once
        session.getFactory().getQueryPlanCache().clear();
        return session.createQuery( query.getQueryString(), query.getResultType() ).list();
    }
    throw e;
}

Prevention

When it happens

Trigger: Executing a query whose SQL AST was built with JdbcParameter instances that never got registered in JdbcParameterBindingsImpl - typical of custom SqlAstTranslators, custom functions or dialect extensions that create parameters during rendering but skip binding registration; also cached query plans executed with bindings prepared against a different plan.

Common situations: Upgrading Hibernate with a stale query plan cache; custom dialect/function code creating parameters on one path and binding on another; concurrency bugs that reuse query plans across mismatched translations. When hit with vanilla mappings this message usually marks a Hibernate bug.

Related errors


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