2010-01-20 35 views
14

我有一個數據庫,一組行,我想提供通過它們旋轉這樣的接口:斯卡拉:通過發電機暴露一個JDBC結果(迭代器)

def findAll: Iterable[MyObject] 

我們不需要同時在內存中存儲所有實例。在C#中,您可以使用yield輕鬆創建類似這樣的生成器,編譯器負責將遍歷記錄集的代碼轉換爲迭代器(將其反轉)。

我當前的代碼如下所示:

def findAll: List[MyObject] = { 
    val rs = getRs 
    val values = new ListBuffer[MyObject] 
    while (rs.next()) 
    values += new valueFromResultSet(rs) 
    values.toList 
} 

有沒有一種方法,我可以將它轉換爲不存儲在內存中整個集合?也許我可以用一個理解?

回答

13

嘗試擴展Iterator。我沒有測試過,但這樣的事情:

def findAll: Iterator[MyObject] = new Iterator[MyObject] { 
    val rs = getRs 
    override def hasNext = rs.hasNext 
    override def next = new valueFromResultSet(rs.next) 
} 

這應該存儲RS當它被稱爲,否則只是一個光包裝來調用RS。

如果您想保存所穿過的值,請查看Stream。

+0

我會給出一個鏡頭,thx Rex – 2010-01-20 16:33:16

+0

我只是假設rs實際上有一個「hasNext」方法。如果沒有,你應該在迭代器中緩存下一個結果(可能是私有的),並讓hasNext指出是否存在緩存的結果。 – 2010-01-20 16:40:15

+1

是的,那有用,我用hasNext = rs.isLast。一個麻煩是,雖然我沒有任何機制來關閉rs和連接。在我當前的代碼中,我已經將(上面的)代碼封裝在一個「使用」方法中,該方法爲我關閉了一些東西。 – 2010-01-20 16:42:25

12

我遇到了同樣的問題,思想的基礎上我上面簡單地寫一個適配器類創建了以下解決方案:

class RsIterator(rs: ResultSet) extends Iterator[ResultSet] { 
    def hasNext: Boolean = rs.next() 
    def next(): ResultSet = rs 
} 

有了這個,你可以如執行地圖上的結果集操作 - 這是我個人的意向:

val x = new RsIterator(resultSet).map(x => { 
    (x.getString("column1"), x.getInt("column2")) 
}) 

,以迫使評估附加一個.toList。如果在使用這些值之前數據庫連接已關閉,這很有用。否則,您會收到一個錯誤消息,說您連接關閉後無法訪問ResultSet。

+0

我用這個解決方案。非常感謝!我確實需要調用toList來使用結果。 val externalKeys = resultSet.map {x => x.getString(「external_key」) } .toList – JasonG 2014-04-09 18:30:47

+0

這有一個問題。按照hasNext規範,它應該在不推進結果集的情況下執行。因此,無論是@Rex Kerr建議的緩存還是使用isLast(不確定性能)都是更好的選擇。 – Sohaib 2017-11-22 15:14:30

6

一個更簡單的(慣用語)的方式來達到同樣會

Iterator.continually((rs.next(), rs)).takeWhile(_._1).map(r => valueFromResultSet(r._2)).toList 

您需要.toList強制評估,否則底層集合將是一個流和評估已經發生前ResultSet中可能被關閉。