mybatis/mybatis-3 · error · ExecutorException

The JDBC Type must be specified for output parameter. Param

Error message

The JDBC Type must be specified for output parameter.  Parameter: {}

What it means

For CALLABLE statements, every OUT or INOUT parameter must be registered with the driver via registerOutParameter, which requires a JDBC type. MyBatis validates this before registration: if a ParameterMapping has mode OUT/INOUT but jdbcType == null, it throws this ExecutorException naming the parameter property.

Source

Thrown at src/main/java/org/apache/ibatis/executor/statement/CallableStatementHandler.java:105

    if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      return connection.prepareCall(sql);
    }
    return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
  }

  @Override
  public void parameterize(Statement statement) throws SQLException {
    registerOutputParameters((CallableStatement) statement);
    parameterHandler.setParameters((CallableStatement) statement);
  }

  private void registerOutputParameters(CallableStatement cs) throws SQLException {
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    for (int i = 0, n = parameterMappings.size(); i < n; i++) {
      ParameterMapping parameterMapping = parameterMappings.get(i);
      if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
        if (null == parameterMapping.getJdbcType()) {
          throw new ExecutorException(
              "The JDBC Type must be specified for output parameter.  Parameter: " + parameterMapping.getProperty());
        }
        if (parameterMapping.getNumericScale() != null && (parameterMapping.getJdbcType() == JdbcType.NUMERIC
            || parameterMapping.getJdbcType() == JdbcType.DECIMAL)) {
          cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getNumericScale());
        } else {
          if (parameterMapping.getJdbcTypeName() == null) {
            cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE);
          } else {
            cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE,
                parameterMapping.getJdbcTypeName());
          }
        }
      }
    }
  }

}

View on GitHub (pinned to 008069adb1)

Solutions

  1. Add an explicit jdbcType to the OUT/INOUT parameter: <parameter property="result" mode="OUT" jdbcType="INTEGER"/> (or in annotations: #{result, mode=OUT, jdbcType=INTEGER}).
  2. If the type is driver-specific, also set typeName for named types via jdbcTypeName (e.g. Oracle STRUCT/ARRAY types).
  3. For INOUT parameters with null input values, always specify jdbcType so both binding and registration have a type.

Example fix

<!-- before -->
<select id="calc" statementType="CALLABLE">{ call calc(#{x, mode=INOUT}, #{total, mode=OUT}) }</select>

<!-- after -->
<select id="calc" statementType="CALLABLE">{ call calc(#{x, mode=INOUT, jdbcType=INTEGER}, #{total, mode=OUT, jdbcType=DECIMAL}) }</select>
Defensive patterns

Strategy: validation

Validate before calling

// before executing, assert every OUT/INOUT parameter mapping declares a jdbcType
List<ParameterMapping> pms = boundSql.getParameterMappings();
for (ParameterMapping pm : pms) {
  if ((pm.getMode() == ParameterMode.OUT || pm.getMode() == ParameterMode.INOUT)
      && pm.getJdbcType() == null) {
    throw new IllegalStateException("OUT/INOUT parameter '" + pm.getProperty()
        + "' is missing jdbcType");
  }
}

Prevention

When it happens

Trigger: Calling a stored procedure ({call ...}) with parameterMode="OUT" or "INOUT" on <parameter property="outParam" .../> (or @Param-driven parameter map) where the jdbcType attribute is missing and no default JDBC type can be inferred for null.

Common situations: Stored procedure mappings copied from IN-only examples without jdbcType; relying on defaults that exist for IN params but not OUT; NULL INOUT params where the driver cannot infer the type so MyBatis cannot fall back.

Related errors


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/ff605369f0efdd18. Report an issue: GitHub.