2011-10-28 76 views
2

我有一個方法來從數據庫中獲取對象的負載,它返回一個IterableChunking一個迭代

現在,我從數據庫加載結果集,從中構建對象並用這些對象填充集合。

顯然,我的內存限制了可以使用這種方法加載多少數據,並且如果我耗盡了壞事情發生。

我想修改實現來從數據庫中分塊數據,而不是一次全部獲取數據,然後通過Iterable接口將產生的對象公開給客戶端。我的數據庫驅動程序可以做到這一點,所以我的第一個想法是這樣做的自定義實現Iterable

這是一個很好的方法嗎?它引起了我在運行時或庫中可能已經支持的一些東西 - 不涉及ORM解決方案。

+2

是否有你想要實現'Iterable'而不僅僅是'Iterator'的原因?後者可能會更容易,因爲您不必添加重新啓動迭代的功能。 –

+0

好點 - 根本沒有理由 – Brabster

回答

3

就我個人而言,我能想到的最簡單的解決方案是實現Iterator作爲圍繞ResultSet的薄包裝。這有以下幾個優點:

  • 你並不需要提供可再現的SQL語句(你可以流未分選的結果,例如)
  • 你並不需要依靠repeatable read,這可能是昂貴的
  • 如果你的JDBC驅動程序是好的,那麼你可以只使用其流結果要素(警告:一些JDBC驅動程序始終只要你開始遍歷它搶了完整的結果)
  • 你並不需要實現重新啓動IteratorIterable.iterator()可能會被調用兩次,這使得這很複雜)。
  • 沒有「記憶」先前返回的數據意味着,存儲需求可以保持相當低的

它也有一些缺點:

  • Iterator實現有效地變成了一個外部的資源,因爲它綁定JDBC資源:它必須以某種方式「關閉」,使其難以使用
  • 如果Iterator掛起較長時間,那麼讓JDBC Connection四處流竄,這可能在其他地方需要(您不能將它返回到游泳池,直到完成Iterator)。

的另一種方式是實現一個List(或Collection)如需要的懶惰地恢復其數據的級分。這可以更好地使用,但構建起來要複雜得多(正確!)。另外,如果內存約束很重要,那麼您需要添加一個機制來丟棄先前還原的對象。

+0

另一種方法(如你所提到的)將記住偏移量,然後使用'SELECT ... LIMIT M,N'來「滾動」到必要的位置。當然,信息可能會隨時間而改變(另一個事務插入了新行或被刪除),但是這種方法允許您關閉JDBC連接,從而允許DB服務器每分鐘處理更多事務。 –

1

我在我的一個應用程序中實現了Joachim建議的方法。我實現了一個DestroyableIterator接口,其中包含一個destroy()方法,在ResultSet包裝器實現的情況下關閉了ResultSet。 (有些庫提供了這個接口,但我沒有看到爲了3線接口定義而引入庫依賴關係的重點。)

我也抓住了SQLException S和以通過迭代器next()hasNext()方法來傳播它們翻譯成他們(選中)春季DataAccessException秒。

關於堅持資源的觀點是有效的;我使用DestroyableIterator來控制應用程序代碼,因此有各種超時機制可以避免長時間持續保留在實時的ResultSet

+0

在Java 7及更高版本中,我建議實現'AutoClosable'並使用'close()'而不是'destroy()'來獲得額外的arm-block甜度。 –

+0

唯一的問題是你必須使用instanceof測試AutoCloseable。也許值得將兩個接口合併爲AutoCloseableIterator? – Adamski

+0

是的,這就是我的意思:讓'DestroyableIterator'實現'AutoClosable'(此時,我將它重命名爲'AutoClosableIterator' ;-)) –