如何使用 JdbcTemplate 在 Spring 中简洁地调用 PL/SQL 过程并返回其输出变量?

Posted

技术标签:

【中文标题】如何使用 JdbcTemplate 在 Spring 中简洁地调用 PL/SQL 过程并返回其输出变量?【英文标题】:How to succinctly call a PL/SQL procedure and return its output variables in Spring with JdbcTemplate? 【发布时间】:2020-02-04 15:55:23 【问题描述】:

这里是 Oracle 的新手。我的经验是构建将查询发送到数据库并返回“结果集”的 Web 应用程序,例如在 Java 中使用 Spring 和 JdbcTemplate。

例如,下面是一个带有常规 SQL 查询的代码示例,它使用 RowMapper 类将结果集的行转换为 Java 列表:

public List<Widget> findAllWidgets() 
    return jdbcTemplate.query("SELECT * FROM widgets", new WidgetRowMapper());

现在我的任务是编写一个调用 PL/SQL 存储过程的查询。这个过程有两个输入参数(这些我可以处理)和两个输出参数(我们称它们为error_codeerror_message)。我想向数据库发送一个查询,该数据库将 (a) 使用我的输入运行该过程并 (b) 返回两个输出。我同样很乐意将两个输出作为“行”或简单地将它们绑定到两个 Java 变量。

这是我尝试过的,它没有抛出错误,但也没有得到输出值。 Java 变量 errorCodeerrorMessage 保持为空字符串:

public Map<String,String> callMyProcedure() 
    String errorCode="";
    String errorMessage="";
    jdbcTemplate.update("call myprocedure(?,?,?,?)","input1","input2",errorCode,errorMessage);
    return Map.of("errorCode",errorCode,"errorMessage",errorMessage);

问题是:在使用 JdbcTemplate 从 Java 调用过程时,如何捕获 PL/SQL 过程的“OUT”变量的值?

编辑:我接受了不使用 JdbcTemplate 的 Alex 的回答,因为这似乎是更好的方法。我自己的答案确实使用了 JdbcTemplate,但需要更多的代码,所以如果你正在寻找专门回答问题的东西,那就可以了。

【问题讨论】:

【参考方案1】:

您可以使用普通的 JDBC。

final String charlie;
final String zulu;
try (CallableStatement cs = connection.prepareCall("call myprocedure(?,?,?,?,?,?,?,?)")) 
    cs.setString(1, "foo");
    cs.setString(2, "bar");
    cs.setString(3, "baz");
    cs.setString(4, "whisky");
    cs.setString(5, "tango");
    cs.setString(6, "foxtrot");
    cs.registerOutParameter(7, Types.VARCHAR);
    cs.registerOutParameter(8, Types.VARCHAR);
    cs.execute();
    connection.commit(); // optional
    charlie = cs.getString(7);
    zulu = cs.getString(8);

在使用 JDBC 时,使用 getInt 方法和类似方法是很危险的,因为它们会将类型转换为原始类型,并且将零替换为 0。最好使用 (Integer) cs.getObject()。同样,setInt 不支持引用类型。

【讨论】:

【参考方案2】:

您可以在jdbcTemplate下获取连接,并使用get方法获取输出为getNString

try (Connection connection = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
CallableStatement statement = connection.prepareCall("call myprocedure(?,?,?,?,?,?,?,?)");
statement.execute();
statement.getNString(1); // using index or your parameter name

检索指定的 NCHAR、NVARCHAR 或 LONGNVARCHAR 参数的值作为 Java 编程语言中的字符串。

【讨论】:

纯jdbc。 呼叫 statement.registerOutParameter(1, Types.NVARCHAR) 丢失【参考方案3】:

我从older question here 那里找到了一些指导,并想出了这个怪物:

    public Map<String,Object> callMyProcedure() 
        return jdbcTemplate.call(new CallableStatementCreator() 
            @Override
            public CallableStatement createCallableStatement(Connection connection) throws SQLException 
                CallableStatement cs = connection.prepareCall("call myprocedure(?,?,?,?,?,?,?,?)");
                cs.setString(1,"foo");
                cs.setString(2,"bar");
                cs.setString(3,"baz");
                cs.setString(4,"whisky");
                cs.setString(5,"tango");
                cs.setString(6,"foxtrot");
                cs.registerOutParameter(7, Types.VARCHAR);
                cs.registerOutParameter(8, Types.VARCHAR);
                return cs;
            
        ,Arrays.asList(
                new SqlParameter(Types.VARCHAR),
                new SqlParameter(Types.VARCHAR),
                new SqlParameter(Types.VARCHAR),
                new SqlParameter(Types.VARCHAR),
                new SqlParameter(Types.VARCHAR),
                new SqlParameter(Types.VARCHAR),
                new SqlOutParameter("errorCode",Types.VARCHAR),
                new SqlOutParameter("errorMessage",Types.VARCHAR)
        ));
    

它确实工作,但我正在寻找一个可以更简洁地做同样事情的答案。也许自从那个旧答案以来的几年里,Spring 已经为 JdbcTemplate 添加了一个新接口?

【讨论】:

是的,答案是***.com/a/48821425。用纯jdbc不是更方便吗? @Alex78191 我不知道jdbcTemplate 的结束位置和“纯 JDBC”的开始位置,但我不喜欢这个答案的主要原因是它需要大量不提供任何信息的代码(即重新声明输入参数的数据类型),程序员很难阅读,并且容易出现所有代码复制和复制粘贴所需的错误。如果我可以注册输出参数而不是所有输入参数,它将大大有助于提高代码的可读性、可维护性。 纯jdbc意味着使用cs.execute而不是jdbcTemplate.call FYU 你可以在 SqlParameter 和 SqlParameter 中设置任何类型。 @Alex78191 您能否添加一个答案或链接,以说明如何使用纯 JDBC 执行此操作?

以上是关于如何使用 JdbcTemplate 在 Spring 中简洁地调用 PL/SQL 过程并返回其输出变量?的主要内容,如果未能解决你的问题,请参考以下文章

spring boot 系列之三:spring boot 整合JdbcTemplate

使用Spring 框架来操作jdbc

如何使用 spring 的 jdbcTemplate 在 SQL 查询中指定参数

如何使用 Spring Framework 中的 JdbcTemplate 类执行 INSERT 语句

JdbcTemplate 如何自动生成主键

如何在 JdbcTemplate 中查询 List<String>?