Treating an SQL ResultSet like a Scala Stream

前端 未结 10 2238
天涯浪人
天涯浪人 2020-12-02 10:11

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

相关标签:
10条回答
  • 2020-12-02 10:21

    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))
    
    0 讨论(0)
  • 2020-12-02 10:21

    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
        }
      }
    
    0 讨论(0)
  • 2020-12-02 10:23

    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.

    0 讨论(0)
  • 2020-12-02 10:28
    Iterator.continually(rs.next())
      .takeWhile(identity)
      .map(_ => Model(
          id = rs.getInt("id"),
          text = rs.getString("text")
       ))
    
    0 讨论(0)
  • 2020-12-02 10:31

    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())
          }
        }
      }
    
    0 讨论(0)
  • 2020-12-02 10:33

    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
      }
    }
    
    0 讨论(0)
提交回复
热议问题