如何为返回多个ResultSet的存储过程创建H2别名函数?
问题
我目前用H2别名函数模拟存储过程调用,处理返回单个ResultSet的存储过程时一切正常,但现在需要为返回两个ResultSet的存储过程创建别名函数,尝试返回List和SimpleResultSet[]都导致JDBC层报错,不清楚正确实现方式。
我的别名方法代码如下(需要返回两个ResultSet实例及其元数据):
public static ResultSet someMethodName(final Connection connection) throws SQLException { //Metadata for RS1 final SimpleResultSet resultSet1 = new SimpleResultSet(); resultSet1.addColumn("field_1", Types.VARCHAR, 10, 0); resultSet1.addColumn("field_2", Types.VARCHAR, 10, 0); //Metadata for rs2 final SimpleResultSet resultSet2 = new SimpleResultSet(); resultSet1.addColumn("field_1", Types.INTEGER, 10, 0); resultSet1.addColumn("field_2", Types.INTEGER, 10, 0); // 注意:原代码此处写错,应为resultSet2 if (connection.getMetaData().getURL().equals("jdbc:columnlist:connection")) { //How do you return the two ResultSet instances? } //How do you return the two ResultSet instances? }
另外,我在Sybase中调用实际存储过程并处理结果集的代码运行正常,参考如下:
final SqlParameter[] parameters = new SqlParameter[]{ new SqlReturnResultSet("result_1", (rs) -> { listA.add(ObjectA.builder() .field1(rs.getString("field_1")) .field2(rs.getString("field_2")) .build()); }), new SqlReturnResultSet("result_2", (rs) -> { listB.add(ObjectB.builder() .field1((Integer) rs.getObject("field_1")) .field2((Integer) rs.getObject("field_2")) .build()); }), }; final List<ObjectA> listA = new ArrayList<>(); final List<ObjectB> listB = new ArrayList<>(); return new SimpleJdbcCall(jdbcTemplate) .withSchemaName("dbo") .withProcedureName(spName) .withoutProcedureColumnMetaDataAccess() .declareParameters(parameters) .execute(Arrays.stream(parameters) .filter(SqlParameterValue.class::isInstance) .map(SqlParameterValue.class::cast) .collect(Collectors.toMap(SqlParameterValue::getName, SqlParameterValue::getValue)));
请问如何在H2别名方法中正确返回这两个ResultSet实例?
解决方案
H2中返回多个ResultSet的别名函数,需要使用H2提供的**org.h2.tools.MultiResultSet**类——它是java.sql.ResultSet的子类,专门用于封装多个结果集。直接返回List或数组会被JDBC驱动拒绝,因为H2只识别特定的ResultSet实现。
修改后的别名方法代码如下:
import org.h2.tools.MultiResultSet; import java.sql.Connection; import java.sql.SQLException; import java.sql.Types; import org.h2.result.SimpleResultSet; public static ResultSet someMethodName(final Connection connection) throws SQLException { // 初始化第一个结果集(修正原代码的列添加错误) final SimpleResultSet resultSet1 = new SimpleResultSet(); resultSet1.addColumn("field_1", Types.VARCHAR, 10, 0); resultSet1.addColumn("field_2", Types.VARCHAR, 10, 0); // 可在此添加数据行:resultSet1.addRow("val1", "val2"); // 初始化第二个结果集(修正原代码的对象引用错误) final SimpleResultSet resultSet2 = new SimpleResultSet(); resultSet2.addColumn("field_1", Types.INTEGER, 10, 0); resultSet2.addColumn("field_2", Types.INTEGER, 10, 0); // 可在此添加数据行:resultSet2.addRow(1, 2); // 处理H2的元数据请求(特殊连接用于获取结果集结构) if (connection.getMetaData().getURL().equals("jdbc:columnlist:connection")) { return MultiResultSet.create(resultSet1, resultSet2); } // 正常返回封装后的多结果集 return MultiResultSet.create(resultSet1, resultSet2); }
关键说明:
- 修复原代码bug:原代码中给第二个结果集添加列时错误操作了
resultSet1,会导致第一个结果集出现重复列,必须改为操作resultSet2。 - 使用MultiResultSet封装:
MultiResultSet.create()方法可接收多个ResultSet实例,将其封装为H2 JDBC驱动可识别的多结果集对象。 - 元数据分支兼容:当H2通过
jdbc:columnlist:connection请求元数据时,同样需要返回包含两个结果集元数据的MultiResultSet,否则会导致元数据获取失败。
采用这种方式后,你现有的Spring SimpleJdbcCall代码无需修改,即可像在Sybase中一样正常处理两个结果集。
内容的提问来源于stack exchange,提问作者juckky
相关产品推荐
相关产品推荐

