2010-01-20 3 views
14

Ich habe eine Reihe von Zeilen in einer Datenbank erhalten, und ich möchte eine Schnittstelle schaffen, durch sie so spinnen:Scala: Belichten eines JDBC ResultSet durch einen Generator (abzählbaren)

def findAll: Iterable[MyObject] 

Wo wir nicht alle Instanzen gleichzeitig im Speicher haben müssen. In C# können Sie einfach Generatoren wie diesen mit yield erzeugen, der Compiler kümmert sich darum, Code, der das Recordset durchläuft, in einen Iterator umzuwandeln (eine Art, ihn zu invertieren).

Meine aktuellen Code sieht wie folgt aus:

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

Gibt es eine Weise, die ich diese konvertieren konnte nicht im Speicher den gesamten Satz speichern? Vielleicht könnte ich ein für das Verständnis verwenden?

Antwort

13

Versuchen Sie stattdessen, Iterator zu erweitern. Ich habe es nicht getestet, aber so etwas wie dies:

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

Dies sollte rs speichern, wenn es heißt, und sonst nur ein Licht-Wrapper, um Anrufe zu rs sein.

Wenn Sie die Werte, die Sie durchlaufen, speichern möchten, überprüfen Sie Stream.

+0

Ich gebe das eine Chance, thx Rex –

+0

Ich habe nur angenommen, dass rs tatsächlich eine "hasNext" -Methode hat. Wenn nicht, sollten Sie das nächste Ergebnis mit einer (wahrscheinlich privaten) Variablen im Iterator zwischenspeichern und HasNext sagen, ob das Ergebnis im Cache existiert. –

+1

Ja, das funktioniert, ich habe hasNext = rs.isLast verwendet. Ein Problem ist, obwohl ich keinen Mechanismus habe, um die RS und die Verbindung zu schließen. In meinem aktuellen Code habe ich den (oben genannten) Code in eine "using" -Methode verpackt, die Dinge für mich schließt. –

12

stieß ich auf das gleiche Problem und basiert auf den Ideen oben ich die folgende Lösung Klasse einfach durch das Schreiben eines Adapters erstellt:

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

Damit kann man zum Beispiel Karte Operationen auf der Ergebnismenge durchführen - was meine persönliche Absicht war:

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

ein .toList um Anhänge an Auswertung zu erzwingen. Dies ist nützlich, wenn die Datenbankverbindung geschlossen wird, bevor Sie die Werte verwenden. Andernfalls erhalten Sie eine Fehlermeldung, dass Sie nach dem Schließen der Verbindung nicht auf das ResultSet zugreifen können.

+0

Ich habe diese Lösung verwendet. Vielen Dank! Ich musste ToList aufrufen, um die Ergebnisse zu verwenden. val externalKeys = resultSet.map {x => x.getString ("externer_schlüssel") } .toList – JasonG

+0

Es gibt ein Problem damit. Gemäß der Spezifikation für hasNext sollte es ausgeführt werden, ohne das Resultset weiterzugeben.Daher ist entweder Caching wie von @Rex Kerr vorgeschlagen oder isLast (nicht sicher über die Leistung) eine bessere Wahl. – Sohaib

6

Ein einfacherer (idiomatischer) Weg, um das gleiche zu erreichen wäre

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

Sie müssen die .toList Auswertung zu zwingen, da sonst die zugrunde liegende Auflistung ein Strom sein wird und das ResultSet geschlossen werden kann, vor der Auswertung stattgefunden hat .

Verwandte Themen