鉴于类型为rowParser
的{{1}},根据我目前看到的代码示例,您将如何解析来自表RowParser[Photo]
的行列表:
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]]
的解析器(认为更懒惰总是更好),但我只想出了这个:
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
,但我的目标是仅对实际读取的行应用List
。有没有更简单的方法来实现这一目标?
编辑:我知道如果事先知道感兴趣的行数,则应在查询中使用rowParser
。我也知道,在很多情况下,你无论如何都会使用整个结果,所以懒惰不会提高性能。但是可能存在一些情况,您可以节省几个周期,例如如果由于某种原因,您有搜索条件,您不能或不想在SQL中表达。所以我认为奇怪的是,鉴于anorm提供了获取limit
Stream
的方法,我没有找到一种直接的方法来应用SqlRow
。RowParser
。
答案 0 :(得分: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)
}
}
请注意,Play SqlResult
只能是Success / Error,而每行也可以是Success / Error。我只处理第一行,假设其余部分相同。这可能适用于您,也可能不适合您。
答案 1 :(得分:1)
您最好使用limit
和offset
制作较小的(分页)查询。
如果要将(大)结果保留在内存中并从那里流式传输,Anorm需要进行一些修改。然后另一个问题是您的JVM的新内存要求。您将如何处理服务级别的缓存?请注意,以前您可以轻松缓存photos?page=1&size=10
之类的内容,但现在您只有photos
,而缓存技术则不知道如何处理该流。
更糟糕的是,并且可能在JDBC级别上,将流包裹在limit
ed和offset
- ed execute
语句中,只是在幕后对数据库进行多次调用,但是这听起来需要花费大量工作来将Scala生成的Stream代码移植到Java land(与Groovy,jRuby等一起工作),然后在批准的JDBC 5或6路线图上获取它。这个想法可能会因为过于复杂而被避开。
你可以将Stream
包裹在你的整个DAO周围(limit
和offset
诡计会发生),但这听起来比它的价值更麻烦: - )
答案 2 :(得分:1)
当遇到转换为Streams的内置anorm函数试图解析结果集时,我遇到了类似的情况,但遇到了调用堆栈溢出异常。
为了解决这个问题,我选择放弃anorm 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") }
}
这种方法当然有缺点,但是,这解决了我的问题,并且不需要包含任何其他库。