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 может быть закрыт до того, как будет выполнена оценка.