Scala: отображение JDBC ResultSet через генератор (итерабельный)
У меня есть набор строк в базе данных, и я хотел бы предоставить интерфейс для их прокрутки следующим образом:
def findAll: Iterable[MyObject]
Если нам не требуется одновременное использование всех экземпляров в памяти. В С# вы можете легко создавать генераторы, подобные этому с помощью yield, компилятор позаботится о преобразовании кода, который перебирает набор записей в итератор (вроде его инвертирования).
Мой текущий код выглядит следующим образом:
def findAll: List[MyObject] = {
val rs = getRs
val values = new ListBuffer[MyObject]
while ( rs.next() )
values += new valueFromResultSet(rs)
values.toList
}
Есть ли способ преобразовать это, чтобы не хранить весь набор в памяти? Возможно, я мог бы использовать для понимания?
Ответы
Ответ 1
Попробуйте расширить 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.
Ответ 2
Я столкнулся с одной и той же проблемой и на основе вышеизложенных идей я создал следующее решение, просто написав класс адаптера:
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 после того, как соединение было закрыто.
Ответ 3
Более простой (идиоматический) способ достичь того же будет
Iterator.continually((rs.next(), rs)).takeWhile(_._1).map(r => valueFromResultSet(r._2)).toList
Вам нужна .toList
для принудительной оценки, иначе базовая коллекция будет потоком, и ResultSet может быть закрыт до того, как будет выполнена оценка.