【问题标题】:Output parameter as Integer using spring StoredProcedure使用spring StoredProcedure将输出参数作为整数
【发布时间】:2020-01-13 02:08:45
【问题描述】:

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

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

我正在使用来自org.springframework.jdbc.objectStoredProcedure 执行方法,该方法返回一个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 3-1 SQL and PL/SQL Data Type to Oracle and JDBC Mapping Classes(您需要向下滚动一点),它显示了 oracle 数据类型和 java 之间的映射,但似乎我无法让它工作。

【问题讨论】:

    标签: java spring oracle spring-jdbc


    【解决方案1】:

    你好格罗吉亚, 我会试着回答当你调用StoredProcedure类的execute方法时我对Spring StoredProcedure类做了一些无聊的分析它调用了下面的方法

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

    经过一系列的调用,它最终调用了ResultSet类的getObject(int column index)方法,该方法是Oracle依赖的Database provider,mysql完全不同,该方法负责返回对象的数据类型,这是由 Column Row Mapper 调用,它映射每个列名的列,并且使用 StoredProcedure 类的相应值

    **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) 方法,实际决定返回的数据类型。

    代码示例:

    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;
    }
    

    所以回到实际的问题,为什么只有它返回的 BigDecimal Type ResultSet 类的 getObject(int column index) 方法正在为 oracle 驱动程序调用以下方法

    Datum var4 = this.getOracleObject(var1)
    

    根据列的各个数据类型调用不同的访问器,并且每个访问器都在您的情况下实现了 getObject oracle 将所有数据类型(例如 int、float)视为数字数据类型参考 https://www.techonthenet.com/oracle/datatypes.php 所以它使用 NumberCommonAccessor,它总是为所有数据类型(如 int、float)返回 BigDecimal,所以你有显式类型转换,你会像往常一样收到数据类型 BigDecimal。它确实不依赖于 SqlOutParameter 来指定您需要存储过程中的一些输出参数列表,并且它对数据类型没有影响。

    【讨论】:

    • 从内存消耗的角度来看,这不是低效吗?
    • 是的,考虑到内存的观点,它应该是低效的
    【解决方案2】:

    您可以通过调用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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-07
      • 2012-04-07
      • 1970-01-01
      • 2017-05-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多