Ответ 1
Я не тестировал его, но почему бы ему не работать?
new Iterator[String] {
def hasNext = resultSet.next()
def next() = resultSet.getString(1)
}.toStream
Когда я запрашиваю базу данных и получаю ResultSet (только для прямого доступа, только для чтения), ResultSet действует как список строк базы данных.
Я пытаюсь найти способ обработать этот ResultSet, как Scala Stream
. Это позволит выполнять такие операции, как filter
, map
и т.д., Не потребляя большие объемы ОЗУ.
Я реализовал метод tail-recursive для извлечения отдельных элементов, но для этого требуется, чтобы все элементы были в памяти одновременно, проблема, если ResultSet очень велика:
// Iterate through the result set and gather all of the String values into a list
// then return that list
@tailrec
def loop(resultSet: ResultSet,
accumulator: List[String] = List()): List[String] = {
if (!resultSet.next) accumulator.reverse
else {
val value = resultSet.getString(1)
loop(resultSet, value +: accumulator)
}
}
Я не тестировал его, но почему бы ему не работать?
new Iterator[String] {
def hasNext = resultSet.next()
def next() = resultSet.getString(1)
}.toStream
Вспомогательная функция для ответа @elbowich:
def results[T](resultSet: ResultSet)(f: ResultSet => T) = {
new Iterator[T] {
def hasNext = resultSet.next()
def next() = f(resultSet)
}
}
Позволяет использовать вывод типа. Например:.
stmt.execute("SELECT mystr, myint FROM mytable")
// Example 1:
val it = results(stmt.resultSet) {
case rs => rs.getString(1) -> 100 * rs.getInt(2)
}
val m = it.toMap // Map[String, Int]
// Example 2:
val it = results(stmt.resultSet)(_.getString(1))
Это звучит как отличная возможность для неявного класса. Сначала определите неявный класс:
import java.sql.ResultSet
object Implicits {
implicit class ResultSetStream(resultSet: ResultSet) {
def toStream: Stream[ResultSet] = {
new Iterator[ResultSet] {
def hasNext = resultSet.next()
def next() = resultSet
}.toStream
}
}
}
Затем просто импортируйте этот неявный класс везде, где вы выполнили свой запрос, и определили объект ResultSet:
import com.company.Implicits._
Наконец, извлеките данные с помощью метода toStream. Например, получите все идентификаторы, как показано ниже:
val allIds = resultSet.toStream.map(result => result.getInt("id"))
Мне нужно что-то подобное. Основываясь на elbowich очень классный ответ, я немного его обернул, а вместо строки вернул результат (чтобы вы могли получить любой столбец)
def resultSetItr(resultSet: ResultSet): Stream[ResultSet] = {
new Iterator[ResultSet] {
def hasNext = resultSet.next()
def next() = resultSet
}.toStream
}
Мне нужно было получить доступ к метаданным таблиц, но это будет работать для строк таблицы (можно было бы сделать stmt.executeQuery(sql) вместо md.getColumns):
val md = connection.getMetaData()
val columnItr = resultSetItr( md.getColumns(null, null, "MyTable", null))
val columns = columnItr.map(col => {
val columnType = col.getString("TYPE_NAME")
val columnName = col.getString("COLUMN_NAME")
val columnSize = col.getString("COLUMN_SIZE")
new Column(columnName, columnType, columnSize.toInt, false)
})
Поскольку ResultSet - это всего лишь изменяемый объект, который затем переходит к следующему, нам нужно определить нашу собственную концепцию следующей строки. Мы можем сделать это с помощью входной функции следующим образом:
class ResultSetIterator[T](rs: ResultSet, nextRowFunc: ResultSet => T)
extends Iterator[T] {
private var nextVal: Option[T] = None
override def hasNext: Boolean = {
val ret = rs.next()
if(ret) {
nextVal = Some(nextRowFunc(rs))
} else {
nextVal = None
}
ret
}
override def next(): T = nextVal.getOrElse {
hasNext
nextVal.getOrElse( throw new ResultSetIteratorOutOfBoundsException
)}
class ResultSetIteratorOutOfBoundsException extends Exception("ResultSetIterator reached end of list and next can no longer be called. hasNext should return false.")
}
EDIT: Перевести на поток или что-то еще, как описано выше.
Эта реализация, хотя и более продолжительная и неуклюжая, в лучшем соответствии с контрактом ResultSet. Побочный эффект был удален из hasNext (...) и переместился в next().
new Iterator[String] {
private var available = resultSet.next()
override def hasNext: Boolean = available
override def next(): String = {
val string = resultSet.getString(1)
available = resultSet.next()
string
}
}
Я думаю, что большинство из вышеперечисленных реализаций имеет недетерминированный метод hasNext
. Вызов его два раза приведет к перемещению курсора во вторую строку. Я бы посоветовал использовать что-то вроде этого:
new Iterator[ResultSet] {
def hasNext = {
!resultSet.isLast
}
def next() = {
resultSet.next()
resultSet
}
}