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

How to succinctly call a PL/SQL procedure and return its output variables in Spring with JdbcTemplate?

这里是 Oracle 的新手。我的经验是构建将查询发送到数据库并获得 "result set" 返回的 Web 应用程序,例如在 Java 中使用 Spring 和 JdbcTemplate.

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

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

现在我的任务是编写调用 PL/SQL 存储过程的查询。这个过程有两个输入参数(我可以处理)和两个输出参数(我们称它们为 error_codeerror_message)。我想向数据库发送一个查询,该查询将 (a) 运行 使用我的输入的过程和 (b) return 两个输出。我同样乐意将两个输出作为 "row" 或简单地将它们绑定到两个 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" 变量的值?

编辑:我接受了 Alex 不使用 JdbcTemplate 的回答,因为这似乎是更好的方法。我自己的回答 确实 使用 JdbcTemplate 但需要更多代码,因此如果您要搜索专门回答问题的内容,那就可以了。

我从 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 添加了一个新接口?

您可以在 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

Retrieves the value of the designated NCHAR, NVARCHAR or LONGNVARCHAR parameter as a String in the Java programming language.

你可以使用普通的 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不支持引用类型。