使用 spring StoredProcedure 将参数输出为 Integer

Output parameter as Integer using spring StoredProcedure

我试图将 Oracle 存储过程的输出参数存储在我的变量中,但我遇到了这个问题:

java.math.BigDecimal cannot be cast to java.lang.Integer

我正在使用 org.springframework.jdbc.object 中的 StoredProcedure 执行方法 returns a Map<String, Object> 然后我做这样的事情

Integer proposalId = procedure.execute().get("po_proposal_id");

其中 po_proposal_id 在 PROCEDURE 中定义为

po_proposal_id          Out Number,

我知道我可以做这样的事情:

Integer proposalId = ((BigDecimal) 
        procedure.execute().get("po_proposal_id")).intValue();

但我不想。我为什么要那么做? Spring StoredProcedure 中的执行方法是否总是将 Number SQL 参数映射为 BigDecimal?

我已尝试将过程中的参数声明为

po_proposal_id          Out Integer,

但运气不好。

而且我还尝试更改将 Java 中的输出参数从 oracle.types.Number 映射到 oracle.types.Integer 的方式,但没有成功。

我发现这个 table Table 3-1 SQL and PL/SQL Data Type to Oracle and JDBC Mapping Classes(你需要向下滚动一点)它显示了 oracle 数据类型和 java 之间的映射,但我似乎不能让它工作。

嗨,格罗贾, 我会尝试回答我对 Spring StoredProcedure class 做了一些无聊的分析,当你调用 StoredProcedure class 的执行方法时,它会调用下面的方法

getJdbcTemplate().call(newCallableStatementCreator(inParams), getDeclaredParameters())

经过一系列调用,它最终调用了 ResultSet class 的 getObject(int column index) 方法,它依赖于 oracle 的数据库提供程序,mysql 它完全不同,这个方法负责对于 returning 对象的数据类型,这是由 Column Row Mapper 调用的,它映射每个列名的列以及使用 StoredProcedure Class

的相应值
**Column Row Mapper Class :**

@Override
public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
    ResultSetMetaData rsmd = rs.getMetaData();
    int columnCount = rsmd.getColumnCount();
    Map<String, Object> mapOfColumnValues = createColumnMap(columnCount);
    for (int i = 1; i <= columnCount; i++) {
        String column = JdbcUtils.lookupColumnName(rsmd, i);
        mapOfColumnValues.put(getColumnKey(column), getColumnValue(rs, i));
    }
    return mapOfColumnValues;
}

getColumnValue(rs, i) 负责调用getResultSetValue(ResultSet rs, int index) 方法负责调用resultSet 的getObject(int column index) 实际确定数据类型returned.

代码示例:

public static Object getResultSetValue(ResultSet rs, int index) throws SQLException {
    Object obj = rs.getObject(index);
    String className = null;
    if (obj != null) {
        className = obj.getClass().getName();
    }
    if (obj instanceof Blob) {
        Blob blob = (Blob) obj;
        obj = blob.getBytes(1, (int) blob.length());
    }
    else if (obj instanceof Clob) {
        Clob clob = (Clob) obj;
        obj = clob.getSubString(1, (int) clob.length());
    }
    else if ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ".equals(className)) {
        obj = rs.getTimestamp(index);
    }
    else if (className != null && className.startsWith("oracle.sql.DATE")) {
        String metaDataClassName = rs.getMetaData().getColumnClassName(index);
        if ("java.sql.Timestamp".equals(metaDataClassName) || "oracle.sql.TIMESTAMP".equals(metaDataClassName)) {
            obj = rs.getTimestamp(index);
        }
        else {
            obj = rs.getDate(index);
        }
    }
    else if (obj instanceof java.sql.Date) {
        if ("java.sql.Timestamp".equals(rs.getMetaData().getColumnClassName(index))) {
            obj = rs.getTimestamp(index);
        }
    }
    return obj;
}

所以真正的问题是为什么只有它的 returning BigDecimal Type ResultSet 的 getObject(int column index) 方法 class 正在为 oracle 驱动程序调用以下方法

Datum var4 = this.getOracleObject(var1)

它根据列的各个数据类型调用不同的访问器,并且每个访问器都在您的案例中实现了 getObject oracle 考虑所有数据类型,例如 int、float 作为数字数据类型参考 https://www.techonthenet.com/oracle/datatypes.php 所以它使用 NumberCommonAccessor,它总是 return BigDecimal 用于所有数据类型,如 int、float 所以你有 显式类型转换 并且你将一如既往地接收数据类型 BigDecimal。它不依赖于指定您需要来自存储过程的一些输出参数列表的 SqlOutParameter,它对数据类型没有影响。

您可以通过调用storedProcedure.declareParameter(SqlParameter param)

来注册参数声明

SqlParameter 有很多构造函数,其中一个包含 org.springframework.jdbc.core.SqlReturnType 接口,因此您可以创建自己的 ReturnType:

public class IntegerReturnType implements SqlReturnType {

  @Override
  public Object getTypeValue(
      final CallableStatement cs,
      final int paramIndex,
      final int sqlType,
      final String typeName
  )
      throws SQLException {
    return cs.getInt(paramIndex);
  }
}

终于可以声明它并调用过程了:

storedProcedure.declareParameter(
  new SqlOutParameter(
    "PO_PROPOSAL_ID", // in Oracle prefers upper case letters
    Types.NUMERIC,
    null,
    new IntegerReturnType()
  )
);

final Integer result = (Integer)(storedProcedure.execute().get("PO_PROPOSAL_ID")); //  Oracle prefers upper case letters