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

发布于 2024-08-18 16:56:53 字数 448 浏览 6 评论 0原文

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

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 来理解?

I've got a set of rows in a database, and I'd like to provide an interface to spin through them like this:

def findAll: Iterable[MyObject]

Where we don't require having all the instances in memory at once. In C# you can easily create generators like this using yield, the compiler takes care of converting code that loops through the recordset into an iterator (sort of inverting it).

My current code looks like this:

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

Is there a way I could convert this to not store the entire set in memory? Perhaps I could use a for comprehension?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

分開簡單 2024-08-25 16:56:53

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

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。

I came across the same problem and based on the ideas above I created the following solution by simply writing an adapter class:

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

With this you can e.g. perform map operations on the result set - which was my personal intention:

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

Append a .toList in order to force evaluation. This is useful if the database connection is closed before you use the values. Otherwise you will get an error saying that you cannot access the ResultSet after the connection was closed.

渔村楼浪 2024-08-25 16:56:53

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

Try extending Iterator instead. I haven't tested it, but something like this:

def findAll: Iterator[MyObject] = new Iterator[MyObject] {
  val rs = getRs
  override def hasNext = rs.hasNext
  override def next = new valueFromResultSet(rs.next)
}

This should store rs when it's called, and otherwise just be a light wrapper to calls to rs.

If you want to save the values that you traverse, check out Stream.

岁吢 2024-08-25 16:56:53

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

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

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

A simpler (idiomatic) way to achieve the same would be

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

You need the .toList to force evaluation, otherwise the underlying collection will be a stream and the ResultSet may be closed before evaluation has taken place.

仅此而已 2024-08-25 16:56:53

我在 Scala 2.13 中设计了一个解决方案,通过使用包含 .map() 方法的隐式类来增强 ResultSet

implicit class ResultSetOps(resultSet: ResultSet) {
  def iterator: Iterator[ResultSet] =
    Iterator
      .continually(resultSet)
      .takeWhile(_.next())

  def map[T](toT: ResultSet => T): Iterator[T] =
    iterator.map(toT)
}

对于较少数量的行,要这样使用:

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

      marketId
    })
    .toList

记住最后的 .toList 至关重要,因为它会导致 Iterator 遍历 ResultSet< /代码>。


对于不希望耗尽内存的大量行,替代方法是使用 .foreach() 进行工作,如下所示:

val ts =
  resultSet
    .iterator
    .foreach(resultSet => {
      val marketId = resultSet.getString("market_id")
      println(s"marketId=$marketId")
      
      //perform and (side-effect) persist the row here
    })

I devised a solution in Scala 2.13 by enhancing ResultSet with an implicit class containing a .map() method.

implicit class ResultSetOps(resultSet: ResultSet) {
  def iterator: Iterator[ResultSet] =
    Iterator
      .continually(resultSet)
      .takeWhile(_.next())

  def map[T](toT: ResultSet => T): Iterator[T] =
    iterator.map(toT)
}

To be used like this for smaller quantities of rows:

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

      marketId
    })
    .toList

It is critically important to remember the .toList at the end as that causes the Iterator to traverse the ResultSet.


For larger quantities of rows where it is undesirable to use up the memory, the alternative is to do the work with a .foreach(), like this:

val ts =
  resultSet
    .iterator
    .foreach(resultSet => {
      val marketId = resultSet.getString("market_id")
      println(s"marketId=$marketId")
      
      //perform and (side-effect) persist the row here
    })

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文