Scala Squeryl:显式运行查询

Scala Squeryl:显式运行查询,scala,orm,playframework,playframework-2.0,squeryl,Scala,Orm,Playframework,Playframework 2.0,Squeryl,当我在squeryl中创建一个查询时,它返回一个query[T]对象。当我迭代查询对象(query[T]扩展Iterable[T])时,该查询尚未执行,将被执行 在执行查询时,必须有事务{}或内部事务{}块 我只是说选择查询和事务是不必要的,但是squeryl框架需要它们 我想在我的应用程序模型中创建一个查询,并将其直接传递给视图,模板中的视图助手在视图中迭代查询并显示数据。 这仅在将事务{}块放入控制器中时才可能(控制器包含模板的调用,因此执行迭代的模板也在其中)。不可能将事务{}块放在模型中

当我在squeryl中创建一个查询时,它返回一个query[T]对象。当我迭代查询对象(query[T]扩展Iterable[T])时,该查询尚未执行,将被执行

在执行查询时,必须有事务{}或内部事务{}块

我只是说选择查询和事务是不必要的,但是squeryl框架需要它们

我想在我的应用程序模型中创建一个查询,并将其直接传递给视图,模板中的视图助手在视图中迭代查询并显示数据。 这仅在将事务{}块放入控制器中时才可能(控制器包含模板的调用,因此执行迭代的模板也在其中)。不可能将事务{}块放在模型中,因为模型并不真正执行查询

但据我所知,该交易与控制器无关。它决定了使用哪种数据库框架的模型、如何使用它以及在何处使用事务。所以我希望事务{}块在模型中

我知道,我可以调用Iterable[T].toList处理这个查询[T]对象,而不是返回查询[T]实例,然后返回创建的列表。然后在模型中执行整个查询,一切正常。但是我不喜欢这种方法,因为从数据库请求的所有数据都必须缓存在这个列表中。我更喜欢将这些数据直接传递到视图的方式。我喜欢MySql在结果集很大时对其进行流式处理的特性


有可能吗?可能类似于函数Query[T].executeNow()的功能,它将请求发送到数据库,能够关闭事务,但仍然使用MySQL流功能,并在访问结果集时接收剩余的(选定的,因此是固定的)结果集?因为结果集在查询时是固定的,所以关闭事务不应该是一个问题。

我在这里看到的一般问题是,您试图结合以下两个想法:

  • 数据的惰性计算;这里:数据库结果

  • 隐藏必须在计算完成时触发的后处理操作的需要;此处:从控制器或视图隐藏必须关闭数据库会话

由于您的计算是惰性的,并且您不必执行到最后(这里:迭代整个结果集),因此没有明显的钩子可以触发后处理步骤

您建议调用
Query[T].toList
不会出现此问题,因为计算会一直执行到最后,请求结果集的最后一个元素可以用作关闭会话的触发器

也就是说,我能想到的最好的方法是下面的代码,它是对
org.squeryl.dsl.QueryDsl.\u中的代码的改编,使用

class IterableQuery[T](val q: Query[T]) extends Iterable[T] {
  private var lifeCycleState: Int = 0
  private var session: Session = null
  private var prevSession: Option[Session] = None

  def start() {
    assert(lifeCycleState == 0, "Queries may not be restarted.")
    lifeCycleState = 1

    /* Create a new session for this query. */
    session = SessionFactory.newSession

    /* Store and unbind a possibly existing session. */
    val prevSession = Session.currentSessionOption
    if(prevSession != None) prevSession.get.unbindFromCurrentThread

    /* Bind newly created session. */
    session.bindToCurrentThread
  }

  def iterator = {
    assert(lifeCycleState == 1, "Query is not active.")
    q.toStream.iterator
  }

  def stop() {
    assert(lifeCycleState == 1, "Query is not active.")
    lifeCycleState = 2

    /* Unbind session and close it. */
    session.unbindFromCurrentThread
    session.close

    /* Re-bind previous session, if it existed. */
    if(prevSession != None) prevSession.get.bindToCurrentThread
  }
}
客户端可以按如下方式使用查询包装器:

var manualIt = new IterableQuery(booksQuery)
manualIt.start()
manualIt.foreach(println)
manualIt.stop()
//      manualIt.foreach(println) /* Fails, as expected */

manualIt = new IterableQuery(booksQuery) /* Queries can be reused */
manualIt.start()
manualIt.foreach(b => println("Book: " + b))
manualIt.stop()
在创建对象时,即在
IterableQuery
的构造函数内,或者在将对象传递给控制器之前,可以调用
manualIt.start()


但是,以这种方式处理资源(文件、数据库连接等)是非常脆弱的,因为出现异常时不会触发后处理。如果您使用
查看
org.squeryl.dsl.QueryDsl.\u的实现,您将看到一些
尝试。。。最后
IterableQuery中缺少的块

如果您能找到一个有趣/令人惊讶的解决方案,请发布您的解决方案。