我正在使用 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
,但也没有用。
这里的问题是 Oracle 执行存储过程的方式不符合 JDBC。 Oracle的SP通过OUT参数返回结果集数据或者返回游标值,并且必须对它们进行特殊处理。这意味着你不能使用任何 Spring 的 JDBC 东西(假设符合 JDBC),你必须自己做。
在实践中,这意味着您必须使用
JdbcTemplate
和 CallableStatementCallback
,这意味着需要比您理想中更多的手动 JDBC 编码,但我还没有找到避免这种情况的方法。
顺便说一句,我相当怀疑 JDBC 规范的编写是为了严格遵守 Sybase(以及 SQL Server)的做事方式,因为 JDBC 中处理存储过程的方式非常适合这些系统(并且不太适合 Oracle 的系统)。
如果在声明作为 CURSOR 的 outparam 时不传递 RowMapper,问题很简单。 Spring 即将返回
{}
即空光标。
declareParameter(new SqlOutParameter("output", OracleTypes.CURSOR)); - returns empty {}
declareParameter(new SqlOutParameter("output", OracleTypes.CURSOR, new ApplicationMapper()); - returns result
ApplicationMapper 是我的自定义映射器,它实现了 RowMapper。
我制作了一个简单的 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>