When I query a database and receive a (forward-only, read-only) ResultSet back, the ResultSet acts like a list of database rows.
I am trying to find some way to trea
Utility function for @elbowich's answer:
def results[T](resultSet: ResultSet)(f: ResultSet => T) = {
new Iterator[T] {
def hasNext = resultSet.next()
def next() = f(resultSet)
}
}
Allows you to use type inference. E.g.:
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))
I think most of above implementations has a nondeterministic hasNext
method. Calling it two times will move cursor to the second row. I would advise to use something like that:
new Iterator[ResultSet] {
def hasNext = {
!resultSet.isLast
}
def next() = {
resultSet.next()
resultSet
}
}
Because ResultSet is just a mutable object being navigated by next, we need to define our own concept of a next row. We can do so with an input function as follows:
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: Translate to stream or something else as per above.
Iterator.continually(rs.next())
.takeWhile(identity)
.map(_ => Model(
id = rs.getInt("id"),
text = rs.getString("text")
))
Here is an alternative, similar to Sergey Alaev's and thoredge's solutions, for when we need a solution which honors the Iterator
contract where hasNext
is side-effect free.
Assuming a function f: ResultSet => T
:
Iterator.unfold(resultSet.next()) { hasNext =>
Option.when(hasNext)(f(resultSet), resultSet.next())
}
I've found it useful to have as map
"extension method" on ResultSet
.
implicit class ResultSetOps(resultSet: ResultSet) {
def map[T](f: ResultSet => T): Iterator[T] = {
Iterator.unfold(resultSet.next()) { hasNext =>
Option.when(hasNext)(f(resultSet), resultSet.next())
}
}
}
This implementation, although longer and clumsier it is in better correspondence with the ResultSet contract. The side-effect has been removed from hasNext(...) and moved into 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
}
}