{"record":{"id":"d3f11da97f7d35c4","repo":"hibernate/hibernate-orm","slug":"usertype-does-not-support-reading-callablestatemen","errorCode":null,"errorMessage":"UserType does not support reading CallableStatement parameter values: {}","messagePattern":"UserType does not support reading CallableStatement parameter values: (.+?)","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/internal/UserTypeJdbcTypeAdapter.java","lineNumber":121,"sourceCode":"\n\t\t@Override\n\t\tpublic J extract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {\n\t\t\tfinal J extracted = userType.nullSafeGet( rs, paramIndex, options );\n\t\t\tlogExtracted( paramIndex, extracted );\n\t\t\treturn extracted;\n\t\t}\n\n\t\t@Override\n\t\tpublic J extract(CallableStatement statement, int paramIndex, WrapperOptions options) throws SQLException {\n\t\t\tif ( userType instanceof ProcedureParameterExtractionAware ) {\n\t\t\t\t//noinspection unchecked\n\t\t\t\tfinal J extracted = ( (ProcedureParameterExtractionAware<J>) userType )\n\t\t\t\t\t\t.extract( statement, paramIndex, options.getSession() );\n\t\t\t\tlogExtracted( paramIndex, extracted );\n\t\t\t\treturn extracted;\n\t\t\t}\n\n\t\t\tthrow new UnsupportedOperationException( \"UserType does not support reading CallableStatement parameter values: \" + userType );\n\t\t}\n\n\t\t@Override\n\t\tpublic J extract(CallableStatement statement, String paramName, WrapperOptions options) throws SQLException {\n\t\t\tif ( userType instanceof ProcedureParameterExtractionAware ) {\n\t\t\t\t//noinspection unchecked\n\t\t\t\tfinal J extracted = ( (ProcedureParameterExtractionAware<J>) userType )\n\t\t\t\t\t\t.extract( statement, paramName, options.getSession() );\n\t\t\t\tlogExtracted( paramName, extracted );\n\t\t\t\treturn extracted;\n\t\t\t}\n\n\t\t\tthrow new UnsupportedOperationException( \"UserType does not support reading CallableStatement parameter values: \" + userType );\n\t\t}\n\n\t\tprivate void logExtracted(int paramIndex, J extracted) {\n\t\t\tif ( JdbcExtractingLogging.LOGGER.isTraceEnabled() ) {\n\t\t\t\tif ( extracted == null ) {","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/internal/UserTypeJdbcTypeAdapter.java#L103-L139","documentation":"UserTypeJdbcTypeAdapter.ValueExtractorImpl.extract(CallableStatement, int, WrapperOptions) (UserTypeJdbcTypeAdapter.java:108-122) reads OUT parameters of stored procedures whose Hibernate type is a UserType. It delegates only when the user type implements org.hibernate.type.ProcedureParameterExtractionAware; otherwise it throws this UnsupportedOperationException, meaning OUT values of this custom type cannot be read.","triggerScenarios":"A StoredProcedureQuery declares an OUT/INOUT parameter typed with a UserType (registerStoredProcedureParameter(i, MyType.class ...) or @ProcedureParameter on a typed class) and you call execute()/getOutputParameterValue(i); extraction fails unless the UserType implements ProcedureParameterExtractionAware.extract(CallableStatement, int, SharedSessionContract).","commonSituations":"Domain-specific types (money, codes) reused as stored-procedure parameters; existing UserTypes left unchanged when stored procedures returning those types were introduced.","solutions":["Implement org.hibernate.type.ProcedureParameterExtractionAware<J> on the UserType and provide the positional extract(CallableStatement, int, SharedSessionContract) implementation that reads the underlying JDBC type and converts it.","Change the stored procedure signature to return a basic type (String/numeric) and convert to your domain type in Java.","Replace the UserType mapping with an AttributeConverter over a standard JDBC type, which supports procedure extraction natively."],"exampleFix":"// before - plain UserType: reading OUT param fails\npublic class MoneyType implements UserType<Money> { ... }\nMoney m = query.getOutputParameterValue( 1 );\n\n// after - extraction-aware user type\npublic class MoneyType implements UserType<Money>, ProcedureParameterExtractionAware<Money> {\n    @Override\n    public Money extract(CallableStatement statement, int index, SharedSessionContract session)\n            throws SQLException {\n        return Money.of( statement.getBigDecimal( index ) );\n    }\n    // ... existing UserType methods\n}","handlingStrategy":"type-guard","validationCode":"// Before declaring OUT params of a custom type on a stored procedure\nif ( !( userType instanceof org.hibernate.type.ProcedureParameterExtractionAware<?> ) ) {\n    // change the OUT parameter to a basic type and convert in Java,\n    // or implement extraction on the UserType first\n}","typeGuard":"static boolean supportsProcedureExtraction(org.hibernate.usertype.UserType<?> userType) {\n    return userType instanceof org.hibernate.type.ProcedureParameterExtractionAware<?>;\n}","tryCatchPattern":"try {\n    query.execute();\n    @SuppressWarnings(\"unchecked\")\n    Money m = (Money) query.getOutputParameterValue( 1 );\n} catch ( UnsupportedOperationException e ) {\n    if ( e.getMessage() != null && e.getMessage().contains( \"CallableStatement parameter values\" ) ) {\n        // re-register the OUT param as BigDecimal and convert manually\n    } else throw e;\n}","preventionTips":["When introducing stored procedures over domain-specific types, extend the UserType with ProcedureParameterExtractionAware (both positional and named extract).","Keep an integration test per stored procedure that exercises OUT-parameter retrieval.","Consider AttributeConverter + basic JDBC types for procedure boundaries."],"tags":["hibernate","usertype","stored-procedure","callable-statement","out-parameter"],"backgroundTag":"stored-procedure-parameter-unsupported","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}