Scala:通过生成器(可迭代)公开 JDBC 结果集

问题描述 投票:0回答:4

我在数据库中有一组行,我想提供一个接口来旋转它们,如下所示:

def findAll: Iterable[MyObject]

我们不需要同时将所有实例存入内存。在 C# 中,您可以使用 Yield 轻松创建这样的生成器,编译器负责将循环记录集的代码转换为迭代器(有点反转它)。

我当前的代码如下所示:

def findAll: List[MyObject] = {
  val rs = getRs
  val values = new ListBuffer[MyObject]
  while ( rs.next() ) 
    values += new valueFromResultSet(rs)
  values.toList
}

有没有办法可以将其转换为不将整个集合存储在内存中?也许我可以用 a 来理解?

scala jdbc iterable
4个回答
15
投票

我遇到了同样的问题,根据上面的想法,我通过简单地编写适配器类创建了以下解决方案:

class RsIterator(rs: ResultSet) extends Iterator[ResultSet] {
    def hasNext: Boolean = rs.next()
    def next(): ResultSet = rs
}

有了这个,您可以例如对结果集执行 map 操作 - 这是我个人的意图:

val x = new RsIterator(resultSet).map(x => {
    (x.getString("column1"), x.getInt("column2"))
})

附加

.toList
以强制评估。如果在使用这些值之前关闭数据库连接,这非常有用。否则,您将收到错误消息,指出连接关闭后无法访问 ResultSet。


13
投票

尝试扩展 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。


7
投票

实现相同目标的更简单(惯用)方法是

Iterator.continually((rs.next(), rs)).takeWhile(_._1).map(r => valueFromResultSet(r._2)).toList

您需要

.toList
来强制评估,否则底层集合将是一个流,并且 ResultSet 可能会在评估发生之前关闭。


0
投票

我在 Scala 2.13 中设计了一个解决方案,通过使用包含

ResultSet
方法的
implicit class
来增强
.map()

implicit class ResultSetOps(resultSet: ResultSet) {
  def map[T](toT: ResultSet => T): Iterator[T] =
    Iterator
      .continually(
        (resultSet, resultSet.next()))
      .takeWhile {
        case (_, hasNext) =>
          hasNext
      }
      .map {
        case (resultSet, _) =>
          toT(resultSet)
      }
}

像这样使用:

var ts =
  resultSet
    .map(resultSet => {
      val marketId = resultSet.getString("market_id")
      println(s"marketId=$marketId")

      marketId
    })
    .toList

记住最后的

.toList
至关重要,因为这会导致
Iterator
穿过
ResultSet


可以使用

implicit class
来更简洁,如下所示:

implicit class ResultSetOps(resultSet: ResultSet) {
  def map[T](toT: ResultSet => T): Iterator[T] =
    Iterator
      .continually((resultSet, resultSet.next()))
      .takeWhile(_._2)
      .map(x => toT(x._1))
}
© www.soinside.com 2019 - 2024. All rights reserved.