Spring 的存储过程 - 从过程返回的结果始终为空

问题描述 投票:0回答:3

我正在使用 Spring 的 JdbcTemplate 和 StoredProcedure 类。 我无法让存储过程类为我工作。

我在 Oracle 数据库上有一个存储过程。 它的签名是

CREATE OR REPLACE PROCEDURE PRC_GET_USERS_BY_SECTION
(user_cursor OUT Pkg_Types.cursor_type
 , section_option_in IN Varchar2
 , section_in IN Varchar2) AS ....

哪里

TYPE cursor_type IS REF CURSOR;

我创建了以下存储过程类来从 oracle 过程获取信息

    private class MyStoredProcedure extends StoredProcedure 
{
    public MyStoredProcedure(JdbcTemplate argJdbcTemplate) 
    {
        super(argJdbcTemplate, "PRC_GET_USERS_BY_SECTION");
        declareParameter(new SqlOutParameter("output", OracleTypes.CURSOR));
        declareParameter(new SqlParameter("input1", Types.VARCHAR));
        declareParameter(new SqlParameter("input2", Types.VARCHAR));
        compile();          
    }


    public Map<String, Object> execute() {

        Map<String, Object> inParams = new HashMap<String, Object>();
        inParams.put("input1", "BG");
        inParams.put("input2", "FE");
        Map output = execute(inParams);
        return output;
    }
}

我在我的 DAO 类之一的方法中调用它

    public List<String> getUserListFromProcedure() throws BatchManagerException
{
    MyStoredProcedure sp = new MyStoredProcedure( this.jdbcTemplate );
    Map<String, Object> result = new HashMap<String, Object>();
    try
    {
        result = sp.execute();
    }

    catch( DataAccessException dae) 
    {

    }
    System.out.println(result.size());
    return null;
}

但是地图的大小始终为 0,所以什么也没有返回。 我知道数据库中有符合我的输入条件的行。 另外,我的代码使用

java.sql.CallableStatement
与 oracle 存储过程进行交互 - 所以过程很好。 将
OraceleTypes.CURSOR
与Spring的存储过程混合在一起是不是错误的? 我还能用什么? 我也尝试过
SqlReturnResultSet
,但也没有用。

java oracle-database stored-procedures jdbc spring-jdbc
3个回答
6
投票

这里的问题是 Oracle 执行存储过程的方式不符合 JDBC。 Oracle的SP通过OUT参数返回结果集数据或者返回游标值,并且必须对它们进行特殊处理。这意味着你不能使用任何 Spring 的 JDBC 东西(假设符合 JDBC),你必须自己做。

在实践中,这意味着您必须使用

JdbcTemplate
CallableStatementCallback
,这意味着需要比您理想中更多的手动 JDBC 编码,但我还没有找到避免这种情况的方法。

顺便说一句,我相当怀疑 JDBC 规范的编写是为了严格遵守 Sybase(以及 SQL Server)的做事方式,因为 JDBC 中处理存储过程的方式非常适合这些系统(并且不太适合 Oracle 的系统)。


4
投票

如果在声明作为 CURSOR 的 outparam 时不传递 RowMapper,问题很简单。 Spring 即将返回

{}
即空光标。

 declareParameter(new SqlOutParameter("output", OracleTypes.CURSOR)); - returns empty {}
 declareParameter(new SqlOutParameter("output", OracleTypes.CURSOR, new ApplicationMapper()); - returns result

ApplicationMapper 是我的自定义映射器,它实现了 RowMapper。


0
投票

我制作了一个简单的 library 来隐藏样板部分以获取

OUT
参数的结果,它是类型安全的并且支持引用游标(Maven Central 上提供的库)。它的工作原理超出了 spring-jdbc。对于给定的示例,调用可能如下所示

import static org.morejdbc.OracleSqlTypes.cursor;
import static org.morejdbc.NamedJdbcCall.call;

...
    public record Entity(String id, String value) {
    }
...

Out<List<Entity>> outUserCursor = Out.of(cursor((rs, rowNum) -> {
    // implementation of spring RowMapper: your custom ResultSet mapping here
    return new Entity(rs.getString("id"), rs.getString("value"));
}));

jdbcTemplate.execute(call("PRC_GET_USERS_BY_SECTION")
        .in("section_option_in", "value_of_section_option_in")
        .in("section_in", "value_of_section_in")
        .out("user_cursor", outUserCursor));

// outUserCursor.get() now contains List<Entity>
© www.soinside.com 2019 - 2024. All rights reserved.