繁体   English   中英

使用 jdbc 模板的简单 JDBC 调用调用 oracle 存储过程时面临的问题

[英]Issue Facing in calling oracle stored procedure using Simple JDBC call of jdbc template

实际上,关于以下问题,我现在被困了很长时间。 有没有人通过 Java 实现了 Oracle 存储过程调用,我已经实现了,但出现了以下错误。

Exception occured in startAxonProducerProcess()","stack_trace":"org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL [{call POST_CYCLE_TFO_STAT_PKG.GET_CYCLE_EVNT_PAY_RECORDS(?, ?, ?, ?)}]; SQL state [99999]; error code [17068]; Invalid argument(s) in call; nested exception is java.sql.SQLException: Invalid argument(s) in call
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:89)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:1108)
    at org.springframework.jdbc.core.JdbcTemplate.call(JdbcTemplate.java:1147)
    at org.springframework.jdbc.core.simple.AbstractJdbcCall.executeCallInternal(AbstractJdbcCall.java:412)
    at org.springframework.jdbc.core.simple.AbstractJdbcCall.doExecute(AbstractJdbcCall.java:372)
    at org.springframework.jdbc.core.simple.SimpleJdbcCall.execute(SimpleJdbcCall.java:198)
    at  ...

我的程序语法是:-

PROCEDURE get_cycle_evnt_pay_records (
      RESULT_AXON_TB       OUT AXON_PAYMENT_TAB_T,
      ERROR_INFO           OUT VARCHAR2,
      RESULT               OUT BOOLEAN,
      DEBUG_FLAG        IN OUT BOOLEAN);

AXON_PAYMENT_TAB_T的类型为AXON_PAYMENT_TAB_TTYPE AXON_PAYMENT_TAB_T IS TABLE OF AXON_PAYMENT_RECORDS_T;

我的 Java 代码如下所示:-

  SimpleJdbcCall call1 = new SimpleJdbcCall(jdbcTemplate)
                    .withoutProcedureColumnMetaDataAccess()
                    .withProcedureName(GET_CYCLE_EVENT_PAY_RECORDS_PROCEDURE)
                    .declareParameters(new SqlOutParameter("RESULT_AXON_TB", Types.STRUCT))
                    .declareParameters(new SqlOutParameter("ERROR_INFO", Types.VARCHAR))
                    .declareParameters(new SqlOutParameter("RESULT", Types.BOOLEAN))
                    .declareParameters(new SqlParameter("DEBUG_FLAG", Types.BOOLEAN))
                    .declareParameters(new SqlOutParameter("DEBUG_FLAG", Types.BOOLEAN));
                  
            SqlParameterSource in1 = new MapSqlParameterSource()
                    .addValue("DEBUG_FLAG", true);
            
            Map<String, Object> result1 = call1.execute(in1);

任何人都可以分享关于此问题的任何指示,这将非常有帮助。

我会放弃使用 Spring JDBC 和JdbcTemplate来调用这个存储过程,而只使用普通的 JDBC。

问题不在于对象表类型,而在于OUT BOOLEAN参数。 JDBC 不支持BOOLEAN类型,因此我们必须解决它,而这些解决方法并不适合 Spring JDBC。

我会改用以下代码:

    String plsql =
            "DECLARE\n" +
            "  l_debug_flag     BOOLEAN;\n" +
            "  l_result         BOOLEAN;\n" +
            "BEGIN\n" +
            "  l_debug_flag := sys.diutil.int_to_bool(?);\n" +
            "  package_name_here.get_cycle_evnt_pay_records(?, ?, l_result, l_debug_flag);\n" +
            "  ? := sys.diutil.bool_to_int(l_result);\n" +
            "  ? := sys.diutil.bool_to_int(l_debug_flag);\n" +
            "END;";

    try (Connection connection = jdbcTemplate.getDataSource().getConnection()) {
        Map<String, Class<?>> typeMap = connection.getTypeMap();
        typeMap.put("AXON_PAYMENT_RECORDS_T", AxonPaymentRecords.class);
        connection.setTypeMap(typeMap);
        try (CallableStatement statement = connection.prepareCall(plsql)) {
            statement.setInt(1, 1); // The second 1 here corresponds to the 'true' value you set for DEBUG_FLAG
            statement.registerOutParameter(2, Types.ARRAY, "AXON_PAYMENT_TAB_T");
            statement.registerOutParameter(3, Types.VARCHAR);
            statement.registerOutParameter(4, Types.INTEGER);
            statement.registerOutParameter(5, Types.INTEGER);
            statement.execute();

            // Elements of this array should be AxonPaymentRecords instances.
            Object[] array = (Object[]) statement.getArray(2).getArray();

            String errorInfo = statement.getString(3);
            boolean result = (statement.getInt(4) == 1);
            boolean debugFlagOut = (statement.getInt(5) == 1);
            
            // do something with these values...
        }
    }

这使用了内置DIUTIL包中的两个函数,它们在布尔值和数字之间进行转换,以明显的方式在TRUE / FALSE / NULL1 / 0 / NULL之间转换。 我们无法使用布尔值调用 JDBC,因此我们传入和传出整数值,并在 JDBC 的任一侧在布尔值和整数值之间进行转换。

您将需要编写一个代表您的AXON_PAYMENT_RECORDS_T类型的 Java 类,并且它需要实现SQLData 在上面的代码中,我将其命名为AxonPaymentRecords 您将需要实现SQLDatagetSQLTypeNamereadSQL方法,但除非您计划使用AXON_PAYMENT_RECORDS_T类型的参数调用任何存储过程, AXON_PAYMENT_RECORDS_T您可能可以实现writeSQL以抛出UnsupportedOperationException因为您不需要调用它。

Oracle 类型名称出现在不同的地方:一次是在添加到类型映射时,一次是在注册表值输出参数时,一次是在我上面提到的SQLData的实现中。 您可能需要使用架构所有者来限定这些类型名称,如果它们在包中声明,还需要包名称。 (我猜它们是在一个包中声明的:在你的代码中,你没有在类型定义中包含CREATE OR REPLACE ,并且AXON_PAYMENT_RECORDS_TRECORD的出现让我怀疑这个类型是一个包中的RECORD ,而不是在包外声明的OBJECT 。)同样,您可能还需要使用架构所有者和/或包名称来限定存储过程的名称。

请注意,如果您使用的是 Oracle 11g 或更早版本,您将无法使用在包中声明的类型。 我正在使用 Oracle 18c XE 并且我让这段代码正常工作,尽管我必须AXON_PAYMENT_RECORDS_T类型的定义和关联的 Java 类。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM