分块可迭代

时间:2011-10-28 08:36:17

标签: java iterable

我有一个从数据库中获取对象的方法,该方法返回Iterable

现在,我正在从数据库加载结果集,从中构建对象并使用这些对象填充集合。

显然,我的内存受限于使用此方法可以加载多少数据,以及我是否遇到了Bad Things Happen。

我想将实现从数据库修改为块数据,而不是一次性完成所有操作,然后通过Iterable接口将生成的对象公开给客户端。我的数据库驱动程序可以做到这一点,所以我首先想到的是Iterable的自定义实现。

这是一个好方法吗?它让我觉得可能已经在运行时或库中得到支持 - 请不要涉及ORM解决方案。

2 个答案:

答案 0 :(得分:3)

就我个人而言,我能想到的最简单的解决方案是将Iterator实现为ResultSet的瘦包装器。这有几个好处:

  • 您不需要提供可重现的SQL语句(例如,您可以流式传输未排序的结果)
  • 您不需要依赖repeatable read,这可能会很昂贵
  • 如果你的JDBC驱动程序是好的,那么你可以使用它的流结果功能(警告:一旦你开始迭代它,一些JDBC驱动程序总是抓住完整的结果!)
  • 您无需重新启动IteratorIterable.iterator()可能会被调用两次,这会使其变得复杂。)
  • 不“记住”先前返回的数据意味着内存要求可以保持相当低

它也有一些缺点:

  • 您的Iterator实现有效地成为外部资源,因为它绑定了JDBC资源:它必须以某种方式“关闭”,使其更难使用
  • 如果Iterator闲逛了很长时间,那么会让JDBC Connection一直闲逛,这可能是其他地方需要的(你无法返回)到了游泳池,直到完成Iterator

另一种方法是实现List(或Collection),根据需要懒洋洋地恢复其数据的一部分。这可以更好用,但构建起来要复杂得多(正确!)。此外,如果内存约束很重要,那么您需要添加一种机制来丢弃以前恢复的对象。

答案 1 :(得分:1)

我在我的一个应用程序中实现了Joachim的建议方法。我实现了一个包含DestroyableIterator方法的destroy()接口,在ResultSet包装器实现的情况下关闭了ResultSet。 (有些库提供了这个接口,但我没有看到为了3行接口定义而引入库依赖的重点。)

我还抓住了SQLException并将它们翻译成(未经检查)Spring DataAccessException,以便通过Iterator的next()hasNext()方法传播它们。

关于持有资源的观点是有效的;我使用DestroyableIterator控制了应用程序代码,因此有各种超时机制来避免长时间保持实时ResultSet