【问题标题】:How to check ResultSet for emptiness inside Spring JDBC ResultSetExtractor? [duplicate]如何在 Spring JDBC ResultSetExtractor 中检查 ResultSet 是否为空? [复制]
【发布时间】:2016-09-23 01:03:18
【问题描述】:

似乎ResultSet 空的标准检查在 Spring 的 JDBC ResultSetExtractor 中不起作用。我以以下方式使用它:

    return jdbcTemplate.query(sql, ps -> {
        ps.setLong(1, fooParam1);
        ps.setLong(2, fooParam2);
    }, rs -> {
        return rs.getLong(1);
    });

那么在这种模式下检查ResultSet空虚的正确方法是什么?

【问题讨论】:

  • 为什么不使用 rs.hasNext()?
  • @RadhakrishnaSharmaGorenta ResultSet 类中没有这种方法
  • 当然,您的第一个参数 sql 可能是一个 PreparedStatementCreator 实现,它已经调用了 next(),但是当您使用与API而不在您的问题中发布代码。您可以使用返回Listquery 重载之一尝试您的设置。第一行是包含还是缺失?正如已经证明的那样,Springs 拥有 ResultSetExtractor 实现 do 调用 next()
  • 您的措辞表明ResultSet 的“合成”性质有所不同。那将是一个非常危险的设置。正如已经多次强调的那样,Springs own ResultSetExtractor implementations 将调用next(),如果你设法获得了一个不能正常工作的环境,你应该重新考虑这一点。您的解决方案可能适用于该设置,但这对其他用户没有帮助。

标签: java spring jdbc java-8


【解决方案1】:

接口的角色似乎有些混淆,我看到一些教程做错了。接口RowMapperRowCallbackHandler 分别负责提供一个值。每行产生一个副作用。

因此,他们不负责调用 next(),因为框架将负责推进到下一行而不调用回调,一旦 next() 返回 false,即使推进到第一行也可能发生,在这种情况下,ResultSet 为空。在后一种情况下,query(… RowMapper<T>) 将返回一个空的List,而不调用RowMapper,而query(… RowCallbackHandler) 将什么也不做。

相比之下,ResultSetExtractor 负责处理整个 ResultSet,其中包括适当地调用next()。所以你的问题的前提(“......extractData 的每次调用都会给我新的一行。”)是错误的。所有 JDBC 操作的标准行为是在第一行之前返回 ResultSet,这样第一个 next() 调用要么将当前行指针移动到第一行,要么在 ResultSet 为空时返回 false .因此,如果您期望最多一行,您的查询应该看起来像

return jdbcTemplate.query(sql, ps -> {
        ps.setLong(1, fooParam1);
        ps.setLong(2, fooParam2);
   }, rs -> rs.next()? rs.getLong(1): null);

编辑:此解决方案在标准 MySQLPostgresql 实例/Andremoniy/上测试并正常工作。

EDIT-2:这个解决方案绝对正确,并且在任何地方都能正常工作。但是应该注意JdbcTemplate类中有两种不同的方法:

  1. public <T> T query(String sql, PreparedStatementSetter pss, ResultSetExtractor<T> rse) - 在这个例子中使用它,它总是为rs.getRows() 返回0,确实如此。提供的解决方案对于这种情况是正确的。

  2. public void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch) 有另一种类型的第三个参数,代码类似:

    jdbcTemplate.query(sql, ps -> { ps.setLong(1, fooParam1); ps.setLong(2, fooParam2); }, rs -> { System.out.println(rs.getRow()); });

会有不同的工作方式:rs.getRow() 将返回处理行数,如果不存在行,则永远不会调用 processRow 方法(这第二种方法让我感到困惑)。 /Andremoniy/

【讨论】:

  • 对不起,我不能同意你的看法。 ResultSetExractor 每次都给我extractData 新的ResultSet 对象,所以它在内部某处调用next。此外,如果您在extractData() 内多次调用next(),您将提前移动并中断整个队列结果的处理。关于您的评分,我不会否决您的回答,但看起来不正确,抱歉
  • 当然,它每次都会为您提供一个 ResultSet。这如何证明next() 已在ResultSet 上被调用?它实际上证明了我所说的,这个回调负责处理整个结果集,因为下次调用它时,它会收到一个新的。
  • 显然您混淆了 rowsRowSetRowSet 可以有任意数量的行,而 RowMapper 每行调用一次,换句话说,每次调用都会获得一个新的 rowResultSetExtractor 不是。 ResultSetExtractor每个结果调用一次,由新的ResultSet 表示。由于每个调用都代表一个全新的查询,因此无论您多久(或是否)在前一个查询的 ResultSet(同时已关闭)上调用 next() 都无关紧要。
  • @Andremoniy 你读过ResultSet.getRow() 的JDBC api 文档吗" 注意:对于结果集类型为TYPE_FORWARD_ONLY"的结果集,对getRow 方法的支持是可选的"“SQLFeatureNotSupportedException - 如果 JDBC 驱动程序不支持此方法”
  • @Andremoniy:关于重载,我认为它们确实很不幸。但这就是为什么我在之前的评论中强调“lambda 表达式的形状”,这足以消除歧义。返回值的单个参数 lambda(与 void 不兼容)只能是 ResultSetExtractor。这可能非常微妙,即在您的问题中您使用的是明确的rs -> { return rs.getLong(1); },但是当您将其简化为rs -> rs.getLong(1) 时,它可以与void 兼容,从而丢弃结果,因此可能是RowCallbackHandler也是。
猜你喜欢
  • 2011-01-20
  • 2014-08-15
  • 2011-07-02
  • 2018-07-03
  • 2013-02-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多