简体   繁体   English

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

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

Actually I am stuck with quite a time now regarding the below issue.实际上,关于以下问题,我现在被困了很长时间。 Does anyone has implemented Oracle Stored procedure calling through Java, I have implemented but getting the below error.有没有人通过 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  ...

My procedure syntax is:-我的程序语法是:-

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 is of type TYPE AXON_PAYMENT_TAB_T IS TABLE OF AXON_PAYMENT_RECORDS_T; AXON_PAYMENT_TAB_T的类型为AXON_PAYMENT_TAB_TTYPE AXON_PAYMENT_TAB_T IS TABLE OF AXON_PAYMENT_RECORDS_T;

And my Java code is like below:-我的 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);

Can anyone share any pointers on this, regarding the issue, it will be really helpful.任何人都可以分享关于此问题的任何指示,这将非常有帮助。

I would give up on using Spring JDBC and a JdbcTemplate to call this stored procedure, and just use plain JDBC.我会放弃使用 Spring JDBC 和JdbcTemplate来调用这个存储过程,而只使用普通的 JDBC。

The problem isn't so much the table-of-object type, but the OUT BOOLEAN parameters.问题不在于对象表类型,而在于OUT BOOLEAN参数。 The BOOLEAN type isn't supported by JDBC so we have to work around it, and these workarounds don't fit easily into Spring JDBC. JDBC 不支持BOOLEAN类型,因此我们必须解决它,而这些解决方法并不适合 Spring JDBC。

I would use the following code instead:我会改用以下代码:

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

This uses two functions in the built-in DIUTIL package that convert between booleans and numbers, converting between TRUE / FALSE / NULL and 1 / 0 / NULL in the obvious way.这使用了内置DIUTIL包中的两个函数,它们在布尔值和数字之间进行转换,以明显的方式在TRUE / FALSE / NULL1 / 0 / NULL之间转换。 We can't call into JDBC with boolean values, so we pass integer values in and out, and convert between boolean and integer values on either side of JDBC.我们无法使用布尔值调用 JDBC,因此我们传入和传出整数值,并在 JDBC 的任一侧在布尔值和整数值之间进行转换。

You will need to write a Java class that represents your AXON_PAYMENT_RECORDS_T type, and it will need to implementSQLData .您将需要编写一个代表您的AXON_PAYMENT_RECORDS_T类型的 Java 类,并且它需要实现SQLData In the code above I named it AxonPaymentRecords .在上面的代码中,我将其命名为AxonPaymentRecords You will need to implement the getSQLTypeName and readSQL methods of SQLData , but unless you are planning on calling any stored procedures with parameters of type AXON_PAYMENT_RECORDS_T then you can probably implement writeSQL to throw an UnsupportedOperationException as you won't need to call it.您将需要实现SQLDatagetSQLTypeNamereadSQL方法,但除非您计划使用AXON_PAYMENT_RECORDS_T类型的参数调用任何存储过程, AXON_PAYMENT_RECORDS_T您可能可以实现writeSQL以抛出UnsupportedOperationException因为您不需要调用它。

There are various places that Oracle type names appear: once when adding to the type map, once when registering the table-valued output parameter and once in the implementation of SQLData that I mentioned above. Oracle 类型名称出现在不同的地方:一次是在添加到类型映射时,一次是在注册表值输出参数时,一次是在我上面提到的SQLData的实现中。 You may need to qualify these type names with the schema owner, and also the package name if they are declared within a package.您可能需要使用架构所有者来限定这些类型名称,如果它们在包中声明,还需要包名称。 (I am guessing they are declared within a package: in your code you don't include CREATE OR REPLACE in the definition of the type, and the appearance of RECORD within AXON_PAYMENT_RECORDS_T leads me to suspect that this type is a RECORD within a package, rather than an OBJECT declared outside a package.) Similarly, you may also need to qualify the name of the stored procedure with schema owner and/or package name. (我猜它们是在一个包中声明的:在你的代码中,你没有在类型定义中包含CREATE OR REPLACE ,并且AXON_PAYMENT_RECORDS_TRECORD的出现让我怀疑这个类型是一个包中的RECORD ,而不是在包外声明的OBJECT 。)同样,您可能还需要使用架构所有者和/或包名称来限定存储过程的名称。

Note that if you are using Oracle 11g or older you won't be able to use types declared within a package.请注意,如果您使用的是 Oracle 11g 或更早版本,您将无法使用在包中声明的类型。 I'm using Oracle 18c XE and I got this code working, although I had to make up the definition of the type AXON_PAYMENT_RECORDS_T and the associated Java class.我正在使用 Oracle 18c XE 并且我让这段代码正常工作,尽管我必须AXON_PAYMENT_RECORDS_T类型的定义和关联的 Java 类。

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

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