2010-01-20 12 views
14

Mam zestaw wierszy w bazie danych, i chciałbym, aby zapewnić interfejs do spin przez nich tak:Scala: Odsłanianie JDBC ResultSet przez generator (iterable)

def findAll: Iterable[MyObject] 

Tam, gdzie nie potrzebujemy mieć wszystkich instancji w pamięci naraz. W języku C# można łatwo tworzyć generatory takie jak to przy użyciu yield, kompilator dba o konwersję kodu, który przechodzi przez zestaw rekordów do iteratora (rodzaj odwracania go).

Mój bieżący kod wygląda następująco:

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

Czy istnieje sposób mogę przekonwertować to do nie zapisać cały zestaw w pamięci? Może mógłbym użyć do zrozumienia?

Odpowiedz

13

Spróbuj zamiast tego użyć rozszerzenia Iteratora. Nie testowałem go, ale coś takiego:

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

ten powinien przechowywać rs kiedy to się nazywa, a inaczej po prostu być światłem wrapper do rozmów na RS.

Jeśli chcesz zapisać wartości, które przemierzasz, sprawdź Strumień.

+0

Dam ci ten strzał, thx Rex –

+0

Po prostu założyłem, że rs rzeczywiście ma metodę "hasNext". Jeśli nie, powinieneś buforować następny wynik za pomocą (prawdopodobnie prywatnego) var wewnątrz iteratora i mieć wartośćNa przykład powiedzieć, czy ten buforowany wynik istnieje. –

+1

Tak, to działa, użyłem hasNext = rs.isLast. Problem polega na tym, że nie mam żadnego mechanizmu zamknięcia rs i połączenia. W moim obecnym kodzie zawinąłem (powyższy) kod w "użyciu" metody, która zamyka dla mnie rzeczy. –

12

natknąłem się na ten sam problem i opiera się na idei powyżej stworzyłem następujące rozwiązanie, po prostu pisząc klasy adaptera:

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

Dzięki temu można na przykład wykonać mapę operacji na zbiorze wyników - co było moją osobistą intencją:

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

Dołączenie .toList w celu zmuszenia ocenę. Jest to przydatne, jeśli połączenie z bazą danych zostanie zamknięte przed użyciem wartości. W przeciwnym razie pojawi się błąd informujący, że nie można uzyskać dostępu do ResultSet po zamknięciu połączenia.

+0

Użyłem tego rozwiązania. Dziękuję bardzo! Musiałem zadzwonić do listy, aby użyć wyników. val zewnętrzneKeys = resultSet.map {x => x.getString ("external_key") } .toList – JasonG

+0

Występuje problem z tym. Zgodnie ze specyfikacją dla hasNext, powinna ona zostać uruchomiona bez przesuwania zestawu wyników.Stąd też buforowanie, jak sugeruje @Rex Kerr lub użycie isLast (nie wiem o wydajności) jest lepszym wyborem. – Sohaib

6

Prostszy (idiomatyczne) sposób, aby osiągnąć ten sam byłby

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

potrzebny jest .toList zmusić ocenę, inaczej zbiór bazowy będzie strumień, a wynikowa może być zamknięty przed ewaluacja miała miejsce .

Powiązane problemy