hibernate/hibernate-orm · error · HibernateException
Cannot mix named parameters and REF_CURSOR parameter on Post
Error message
Cannot mix named parameters and REF_CURSOR parameter on PostgreSQL
What it means
PostgreSQLCallableStatementSupport renders the JDBC call string for PostgreSQL. When the function return is a REF_CURSOR and the first parameter is the refcursor placeholder ({? = call f(?)}) the syntax is inherently positional; PostgreSQL cannot mix 'param => ?' named notation with a refcursor placeholder. Hibernate checks parameterMetadata.hasNamedParameters() at that point and fails fast with HibernateException instead of emitting an invalid call string.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/procedure/internal/PostgreSQLCallableStatementSupport.java:90
if ( functionReturn == null && parameterMetadata.hasNamedParameters() ) {
// That's just a rough estimate. I guess most params will have fewer than 8 chars on average
paramStringSizeEstimate = registrations.size() * 10;
}
else {
// For every param rendered as '?' we have a comma, hence the estimate
paramStringSizeEstimate = registrations.size() * 2;
}
final JdbcCallImpl.Builder builder = new JdbcCallImpl.Builder();
final int jdbcParameterOffset;
final int startIndex;
final CallMode callMode;
if ( functionReturn != null ) {
if ( functionReturn.getJdbcTypeCode() == SqlTypes.REF_CURSOR ) {
if ( firstParamIsRefCursor ) {
// validate that the parameter strategy is positional (cannot mix, and REF_CURSOR is inherently positional)
if ( parameterMetadata.hasNamedParameters() ) {
throw new HibernateException( "Cannot mix named parameters and REF_CURSOR parameter on PostgreSQL" );
}
callMode = CallMode.CALL_RETURN;
startIndex = 1;
jdbcParameterOffset = 1;
builder.addParameterRegistration( registrations.get( 0 ).toJdbcParameterRegistration( 1, procedureCall ) );
}
else {
callMode = CallMode.TABLE_FUNCTION;
startIndex = 0;
jdbcParameterOffset = 1;
// Old style
// callMode = CallMode.CALL_RETURN;
// startIndex = 0;
// jdbcParameterOffset = 2;
// builder.setFunctionReturn( functionReturn.toJdbcFunctionReturn( procedureCall.getSession() ) );
}
}
else {View on GitHub (pinned to fad1729dce)
Solutions
- Register every parameter positionally (no names) whenever REF_CURSOR is involved on PostgreSQL.
- Prefer a table function returning SETOF rows and consume results with getResultList() — no REF_CURSOR registration needed.
- If names are required for readability, keep a positional map in your code and bind by position.
Example fix
// before (named + refcursor return -> throws)
proc.registerStoredProcedureParameter("in_user", Long.class, ParameterMode.IN);
proc.registerParameter(0, void.class, ParameterMode.REF_CURSOR); // function return
// after (all positional)
proc.registerStoredProcedureParameter(0, void.class, ParameterMode.REF_CURSOR);
proc.registerStoredProcedureParameter(1, Long.class, ParameterMode.IN); Defensive patterns
Strategy: validation
Validate before calling
// forbid named parameters on a PostgreSQL call that uses REF_CURSOR
boolean hasRefCursor = proc.getParameters().stream()
.anyMatch( p -> p.getMode() == ParameterMode.REF_CURSOR );
boolean hasNamed = proc.getParameters().stream()
.anyMatch( p -> p.getName() != null );
if ( hasRefCursor && hasNamed ) {
throw new IllegalArgumentException( "PostgreSQL: register all parameters positionally when REF_CURSOR is used" );
} Type guard
static boolean usesRefCursor(StoredProcedureQuery q) {
return q.getParameters().stream()
.anyMatch( p -> p.getMode() == ParameterMode.REF_CURSOR );
} Prevention
- On PostgreSQL, register refcursor calls fully positionally — never mix named parameters in.
- Prefer SETOF-returning table functions over refcursor for new code.
- In @NamedStoredProcedureQuery, omit parameter names for PostgreSQL refcursor procedures.
When it happens
Trigger: Registering a REF_CURSOR function return plus any named parameter on PostgreSQL, e.g. proc.registerStoredProcedureParameter("in_user", Long.class, ParameterMode.IN) together with a refcursor return; using ProcedureCall#setParameter by name in the same call.
Common situations: Porting Oracle procedures that combine named parameters with cursor returns; naming parameters for readability while keeping a refcursor API; shared DAO code that always binds by name.
Related errors
- GaussDB only supports accessing REF_CURSOR parameters by pos
- PostgreSQL supports only one REF_CURSOR parameter, but multi
- JDBC driver does not support named parameters for setArray.
- GaussDB only supports REF_CURSOR parameters as the first par
- PostgreSQL only supports REF_CURSOR parameters as the first
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/3dedd613daca272a.
Report an issue: GitHub.