hibernate/hibernate-orm · error · UnsupportedOperationException
Type [${userType}] does support parameter binding by name
Error message
Type [${userType}] does support parameter binding by name What it means
CustomType.nullSafeSet(CallableStatement, value, String, session) (CustomType.java:307-323) can only bind a stored-procedure parameter by NAME when the wrapped UserType implements ProcedureParameterNamedBinder (and its canDoSetting() returns true); otherwise it throws UnsupportedOperationException. Note the message text 'does support parameter binding by name' is a known Hibernate wording bug - it means 'does NOT support'.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/type/CustomType.java:318
return checkable[0] && isDirty( old, current, session );
}
@Override
public boolean canDoSetting() {
return getUserType() instanceof ProcedureParameterNamedBinder<?> procedureParameterNamedBinder
&& procedureParameterNamedBinder.canDoSetting();
}
@Override
public void nullSafeSet(CallableStatement statement, J value, String name, SharedSessionContractImplementor session)
throws SQLException {
if ( canDoSetting() ) {
//noinspection unchecked
( (ProcedureParameterNamedBinder<J>) getUserType() )
.nullSafeSet( statement, value, name, session );
}
else {
throw new UnsupportedOperationException(
"Type [" + getUserType() + "] does support parameter binding by name"
);
}
}
@Override
public boolean canDoExtraction() {
return getUserType() instanceof ProcedureParameterExtractionAware<?> procedureParameterExtractionAware
&& procedureParameterExtractionAware.canDoExtraction();
}
@Override
public J extract(CallableStatement statement, int startIndex, SharedSessionContractImplementor session)
throws SQLException {
if ( canDoExtraction() ) {
//noinspection unchecked
return ((ProcedureParameterExtractionAware<J>) getUserType() )
.extract( statement, startIndex, session );View on GitHub (pinned to fad1729dce)
Solutions
- Implement ProcedureParameterNamedBinder on your UserType: add nullSafeSet(CallableStatement st, J value, String name, SharedSessionContract session) and return true from canDoSetting().
- If you cannot change the type, register the procedure parameter positionally instead of by name so the positional binder is used.
- Alternatively declare the parameter with a basic built-in type (e.g. String) and convert the value at the call site.
- Upgrade Hibernate if affected by the misleading message; parse it as 'does not support' regardless.
Example fix
// before
public class MoneyUserType implements UserType<Money> {
// only implements positional nullSafeSet(PreparedStatement, ...) -> named binding throws
}
// after
public class MoneyUserType implements UserType<Money>, ProcedureParameterNamedBinder<Money> {
@Override public boolean canDoSetting() { return true; }
@Override public void nullSafeSet(CallableStatement st, Money value, String name,
SharedSessionContract session) throws SQLException {
st.setBigDecimal(name, value != null ? value.getAmount() : null);
}
// ... rest of UserType
} Defensive patterns
Strategy: type-guard
Validate before calling
UserType<?> t = resolveUserType(paramType);
if (!(t instanceof org.hibernate.usertype.ProcedureParameterNamedBinder<?> nb)
|| !nb.canDoSetting()) {
// bind positionally or use a basic type instead of a named custom-typed parameter
registerPositionally(call, index, value);
} Type guard
static boolean supportsNamedBinding(UserType<?> t) {
return t instanceof org.hibernate.usertype.ProcedureParameterNamedBinder<?> nb
&& nb.canDoSetting();
} Try / catch
try {
query.setParameter("amount", money);
} catch (UnsupportedOperationException e) {
if (e.getMessage().contains("parameter binding by name")) {
// degrade to positional registration for this custom type
call.bindPositional(1, money);
} else throw e;
} Prevention
- For every UserType that can appear in stored procedures, implement ProcedureParameterNamedBinder and ProcedureParameterExtractionAware up front.
- Keep an integration test that calls a trivial procedure with each custom type, by name and by position.
When it happens
Trigger: A StoredProcedureQuery / ProcedureCall with a named parameter whose Java type is bound through a custom UserType (or AttributeConverter-backed custom type) that does not implement org.hibernate.usertype.ProcedureParameterNamedBinder; binding happens when you setParameter('name', value) on the registered named parameter. Positional binding does not go through this path.
Common situations: Custom UserTypes for things like JSON columns, monetary amounts or enums used as stored-procedure parameters; code migrated from positional to named procedure parameters after a DB refactoring; reusing a UserType written only for plain Statement/PreparedStatement binding.
Related errors
- Type [${userType}] does support parameter value extraction
- Class '" + typeName + "' does not implement '" + supertype.g
- Using UserType for CallableStatement parameter binding not s
- JDBC driver does not support named parameters for setArray.
- GaussDB only supports REF_CURSOR parameters as the first par
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/8a849d303cf87b19.
Report an issue: GitHub.