2

根据我目前看到的代码示例,鉴于rowParsertype RowParser[Photo],这就是你如何解析来自 table 的行列表:photo

def getPhotos(album: Album): List[Photo] = DB.withConnection { implicit c =>
  SQL("select * from photo where album = {album}").on(
    'album -> album.id
  ).as(rowParser *)
}

运算符在其中*创建类型为 的解析器ResultSetParser[List[Photo]]。现在,我想知道是否同样有可能获得一个产生 a 的解析器Stream(认为更懒惰总是更好),但我只是想出了这个:

def getPhotos(album: Album): Stream[Photo] = DB.withConnection { implicit c =>
  SQL("select * from photo where album = {album}").on(
    'album -> album.id
  )() collect (rowParser(_) match { case Success(photo) => photo })
}

它有效,但似乎过于复杂。我当然可以调用我从第一个函数中得到的,但我的目标是只应用于toStream实际读取的行。有没有更简单的方法来实现这一点?ListrowParser

编辑:如果事先知道感兴趣的行数,我知道limit应该在查询中使用它。我也知道,在很多情况下,无论如何你都会使用整个结果,所以懒惰不会提高性能。但是在某些情况下,您可能会节省几个周期,例如,如果由于某种原因,您有无法或不想在 SQL 中表达的搜索条件。所以我觉得奇怪的是,鉴于 anorm 提供了一种获得 a Streamof 的方法SqlRow,我没有找到一种直接的方法来应用 a RowParser

4

3 回答 3

2

我最终创建了自己的stream方法,该方法对应于该list方法:

def stream[A](p: RowParser[A]) = new ResultSetParser[Stream[A]]  {
      def apply(rows: SqlParser.ResultSet): SqlResult[Stream[A]] = rows.headOption.map(p(_)) match {
        case None => Success(Stream.empty[A])
        case Some(Success(a)) => {
          val s: Stream[A] = a #:: rows.tail.flatMap(r => p(r) match {
            case Success(r) => Some(r)
            case _ => None
          })  

          Success(s)
        }
        case Some(Error(msg)) => Error(msg)
      }
   } 

请注意,播放SqlResult只能是成功/错误,而每一行也可以是成功/错误。我只处理第一行,假设其余部分相同。这可能适合您,也可能不适合您。

于 2012-12-18T12:18:00.693 回答
1

您最好使用limitand进行较小的(分页)查询offset

如果您要将(大)结果保存在内存中并从那里流式传输,Anorm 需要进行一些修改。然后另一个问题是 JVM 的新内存要求。您将如何处理服务级别的缓存?看,以前您可以轻松地缓存类似 的photos?page=1&size=10内容,但现在您只有photos,并且缓存技术不知道如何处理流。

更糟糕的是,可能在 JDBC 级别上,将 Stream 包装在limited 和offset-edexecute语句周围,并且只是在幕后多次调用数据库,但这听起来需要相当多的工作来移植 Scala 的 Stream 代码生成到 Java 领域(与 Groovy、jRuby 等一起使用),然后在批准的 JDBC 5 或 6 路线图上获得它。这个想法可能会因为太复杂而被回避,事实就是如此。

你可以环绕Stream你的整个 DAO(limitoffset诡计发生的地方),但这几乎听起来比它的价值更麻烦:-)

于 2012-11-09T08:34:22.670 回答
1

我遇到了类似的情况,但是当用于转换为 Streams 的内置异常函数尝试解析结果集时遇到了调用堆栈溢出异常。

为了解决这个问题,我选择放弃异常的 ResultSetParser 范式,并回退到 java.sql.ResultSet 对象。

我想使用 anorm 的内部类来解析结果集行,但是,从 2.4 版开始,他们已经将所有相关的类和方法设为其包的私有,并弃用了其他几种更直接的方法使用。

我结合使用 Promises 和 Futures 来解决 anorm 现在返回的 ManagedResource。我避免了所有已弃用的功能。

import anorm._
import java.sql.ResultSet
import scala.concurrent._

def SqlStream[T](sql:SqlQuery)(parse:ResultSet => T)(implicit ec:ExecutionContext):Future[Stream[T]] = {
  val conn = db.getConnection()
  val mr = sql.preparedStatement(conn, false)
  val p = Promise[Unit]()
  val p2 = Promise[ResultSet]()
  Future {
    mr.map({ stmt =>
      p2.success(stmt.executeQuery)
      Await.ready(p.future, duration.Duration.Inf)
    }).acquireAndGet(identity).andThen { case _ => conn.close() }
  }
  def _stream(rs:ResultSet):Stream[T] = {
    if (rs.next()) parse(rs) #:: _stream(rs)
    else {
      p.success(())
      Stream.empty
    }
  }
  p2.future.map { rs =>
    rs.beforeFirst()
    _stream(rs)
  }
}

这个函数的一个相当简单的用法是这样的:

def getText(implicit ec:ExecutionContext):Future[Stream[String]] = {
  SqlStream(SQL("select FIELD from TABLE")) { rs => rs.getString("FIELD") }
}

当然,这种方法也有缺点,但是,这解决了我的问题并且不需要包含任何其他库。

于 2015-07-27T01:29:06.027 回答