hibernate/hibernate-orm · error · UnsupportedOperationException

Type [${userType}] does support parameter value extraction

Error message

Type [${userType}] does support parameter value extraction

What it means

CustomType.extract(CallableStatement, int, session) (CustomType.java:327-341) throws UnsupportedOperationException when canDoExtraction() is false, i.e. the wrapped UserType does not implement ProcedureParameterExtractionAware (or its canDoExtraction() returns false). Extraction-by-position is what backs StoredProcedureQuery.getOutputParameterValue(int) for OUT/INOUT parameters, so reading an output parameter typed with a plain UserType fails.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/CustomType.java:339

		}
	}

	@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 );
		}
		else {
			throw new UnsupportedOperationException(
					"Type [" + getUserType() + "] does support parameter value extraction"
			);
		}
	}

	@Override
	public J extract(CallableStatement statement, String paramName, SharedSessionContractImplementor session)
			throws SQLException {
		if ( canDoExtraction() ) {
			//noinspection unchecked
			return ((ProcedureParameterExtractionAware<J>) getUserType() )
					.extract( statement, paramName, session );
		}
		else {
			throw new UnsupportedOperationException(
					"Type [" + getUserType() + "] does support parameter value extraction"
			);
		}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Implement ProcedureParameterExtractionAware on the UserType: add extract(CallableStatement, int, SharedSessionContract) and return true from canDoExtraction().
  2. If the type cannot be changed, register the parameter as a basic type (e.g. String) and convert the returned value yourself.
  3. Check INOUT parameters too - they also pass through extraction after execution.
  4. Keep positional and named extract implementations consistent if you support both call styles.

Example fix

// before
public class JsonUserType implements UserType<Payload> { /* no extraction support */ }

// after
public class JsonUserType implements UserType<Payload>, ProcedureParameterExtractionAware<Payload> {
    @Override public boolean canDoExtraction() { return true; }

    @Override public Payload extract(CallableStatement statement, int startIndex,
                                     SharedSessionContract session) throws SQLException {
        String json = statement.getString(startIndex);
        return json == null ? null : parse(json);
    }

    @Override public Payload extract(CallableStatement statement, String paramName,
                                     SharedSessionContract session) throws SQLException {
        String json = statement.getString(paramName);
        return json == null ? null : parse(json);
    }
    // ... rest of UserType
}
Defensive patterns

Strategy: type-guard

Validate before calling

UserType<?> t = resolveUserType(paramType);
if (!(t instanceof org.hibernate.usertype.ProcedureParameterExtractionAware<?> ea)
        || !ea.canDoExtraction()) {
    // read as String/basic and convert, instead of getOutputParameterValue(int)
    String raw = (String) query.getOutputParameterValue(index);
    return parse(raw);
}

Type guard

static boolean supportsExtraction(UserType<?> t) {
    return t instanceof org.hibernate.usertype.ProcedureParameterExtractionAware<?> ea
        && ea.canDoExtraction();
}

Try / catch

try {
    return (Money) query.getOutputParameterValue(index);
} catch (UnsupportedOperationException e) {
    if (e.getMessage().contains("parameter value extraction")) {
        return parse((String) query.getOutputParameterValue(index));
    }
    throw e;
}

Prevention

When it happens

Trigger: StoredProcedureQuery.registerStoredProcedureParameter(i, MyCustomType.class, ParameterMode.OUT) followed by getOutputParameterValue(i), where the custom type does not implement org.hibernate.usertype.ProcedureParameterExtractionAware; also frameworks that reflectively read all output parameters after executing a call.

Common situations: Custom types for JSONB/enum/monetary columns reused as procedure OUT parameters; migrating from direct JDBC calls (which handled extraction manually) to JPA StoredProcedureQuery; upgrading Hibernate versions where extraction was previously never invoked for these types.

Related errors


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