调用包函数的Java怎么写?对工作 Java 代码稍作改动

How to write Java that calls a package function? Making a slight change to working Java code

在我之前问题的回复中提供的帮助:

我能够调用一个 Oracle 程序包过程,该过程 return 是单个 varchar2(1) 输出参数,但现在我想将该过程更改为具有以下 SPEC return 的 FUNCTION一个字符:

FUNCTION is_running_in_production RETURN CHAR;

我如何修改下面的代码,该代码调用了 return 单个输出参数的过程的早期版本,而不是调用这个调用 return 是一个 CHAR 字段的 FUNCTION 的过程?

public static final String IS_RUNNING_IN_PRODUCTION = "{call FDS_APPS.FDS_USR_SEC_PKG2.is_running_in_production2(?)}";

public String isRunningInProduction() throws DaoException {
        
        String inProduction = "";
        
        try {

            SqlOutParameter isProd = new SqlOutParameter("v_is_prod", OracleTypes.VARCHAR);

            List<SqlParameter> paramList = new ArrayList<SqlParameter>();
            paramList.add(isProd); 
            
            Map<String, Object> resultMap = jdbcTemplate.call(new CallableStatementCreator() {

                public OracleCallableStatement createCallableStatement(Connection connection) throws SQLException {

                    OracleCallableStatement callableStatement = (OracleCallableStatement) connection
                            .prepareCall(IS_RUNNING_IN_PRODUCTION);
                                        
                    callableStatement.registerOutParameter(1, Types.VARCHAR);
                    return callableStatement;
                }
            }, paramList);

            inProduction = (String)resultMap.get("v_is_prod");
                                    
        } catch (Exception e) {
            LOGGER.error("Error while determining if running in prod or not, PROC_NAME::[" + IS_RUNNING_IN_PRODUCTION + "]," + e);

        }
                
        return inProduction;    
        
    }

因为 jdbcTemplate.call 过程似乎需要一个 paramList,所以我注释掉了将 isProd 添加到 paramList 并传递了一个添加了零参数的 paramList。我还将 callableStatement.registerOutParameter 更改为 callableStatement.registerReturnParameter,并将数据类型从 Types.VARCHAR 更改为 Types.CHAR。如果其中任何一个是正确的,我不确定我将如何提取结果以填充 return 变量 inProduction.

public String isRunningInProduction() throws DaoException {
        
        String inProduction = "";
        
        try {

            //SqlOutParameter isProd = new SqlOutParameter("v_is_prod", OracleTypes.VARCHAR);                       
            List<SqlParameter> paramList = new ArrayList<SqlParameter>();
            //paramList.add(isProd);            
            
            Map<String, Object> resultMap = jdbcTemplate.call(new CallableStatementCreator() {

                public OracleCallableStatement createCallableStatement(Connection connection) throws SQLException {

                    OracleCallableStatement callableStatement = (OracleCallableStatement) connection
                            .prepareCall(IS_RUNNING_IN_PRODUCTION);
                                        
                    
                    //callableStatement.registerOutParameter(1, Types.VARCHAR);
                    callableStatement.registerReturnParameter(1, Types.CHAR);

                    return callableStatement;
                }
            }, paramList);

            inProduction = (String)resultMap.get("v_is_prod");  
            
                                    
        } catch (Exception e) {
            LOGGER.error("Error while determining if running in prod or not, PROC_NAME::[" + IS_RUNNING_IN_PRODUCTION + "]," + e);
            throw new DaoException("Error while retreiving " + IS_RUNNING_IN_PRODUCTION + e);
        }
        
        return inProduction;    
        
    }

我在 resultMap 对象初始化时遇到以下错误,甚至在我不得不处理获取结果之前。

org.springframework.jdbc.UncategorizedSQLException: CallableStatementCallback; uncategorized SQLException for SQL []; SQL state [99999]; error code [17090]; operation not allowed; nested exception is java.sql.SQLException: operation not allowed

为了调用一个函数,我建议您在基于 jdbcTemplate.call.

的链接问题中采用类似但不同的方法

例如,您可以尝试使用 JdbcTemplateCallableStatementCreatorexecute 方法,与您的示例中的方法非常相似,但可以更好地控制输出结果:

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

try {
  String result = jdbcTemplate.<String>execute( new CallableStatementCreator() {
        public CallableStatement createCallableStatement(Connection connection)
          throws SQLException {
          CallableStatement cs = connection.prepareCall("{? = call FDS_APPS.FDS_USR_SEC_PKG2.is_running_in_production2}");
          // The first out parameter is the result of the function
          // Set the appropriate type
          cs.registerOutParameter(1, Types.VARCHAR);
          // Set the rest of the arguments, if required
          return cs;
        }
      },
    new CallableStatementCallback<String>() {
      public String doInCallableStatement(CallableStatement cs)  throws SQLException {
        cs.execute();
        String result = cs.getString(1);
        // The value eturned here is the one returned by the execute method
        return result;
      }
    }
  );

  System.out.printf("Result with CSC: '%s'", result);

} catch (Throwable t) {
  // Please, forgive me for this
  t.printStackTrace();
}

或者,用 lambda 表达式简化:

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

try {
  String result = jdbcTemplate.<String>execute(connection -> {
    CallableStatement cs = connection.prepareCall("{? = call FDS_APPS.FDS_USR_SEC_PKG2.is_running_in_production2}");
    // The first out parameter is the result of the function
    // Set the appropriate type
    cs.registerOutParameter(1, Types.VARCHAR);
    // Set the rest of the arguments, if required
    return cs;
  },
    (CallableStatementCallback) cs -> {
      cs.execute();
      String result1 = cs.getString(1);
      // The value returned here is the one returned by the execute method
      return result1;
    }
  );

  System.out.printf("Result with CSC: '%s'", result);

} catch (Throwable t) {
  // Please, forgive me for this
  t.printStackTrace();
}

请注意实际的 SQL 表达式是如何构建的:

{? = call FDS_APPS.FDS_USR_SEC_PKG2.is_running_in_production2}

使用 SimpleJdbcCall class 可以大大简化此代码。请考虑以下示例:

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

try {
  SimpleJdbcCall isRunningInProductionFunction = new SimpleJdbcCall(jdbcTemplate)
    .withSchemaName("FDS_APPS")
    .withCatalogName("FDS_USR_SEC_PKG2")
    .withFunctionName("is_running_in_production2");

  // Indicate the return type. You can pass additional arguments if you need to
  String result = isRunningInProductionFunction.executeFunction(String.class);
  System.out.printf("Result as simple JDBC call: '%s'", result);
} catch (Throwable t) {
  // Please, forgive me for this
  t.printStackTrace();
}

请考虑查看 Spring Data Access documentation:其中有大量示例和替代解决方案。