我在这里看到的一般问题是您尝试结合以下两个想法:
由于您的计算是惰性的,并且由于您没有义务将其执行到最后(这里:迭代整个结果集),因此没有明显的钩子可以触发后处理步骤。
您的调用建议Query[T].toList
不会出现此问题,因为计算执行到最后,并且请求结果集的最后一个元素可以用作关闭会话的触发器。
也就是说,我能想到的最好的方法如下,这是对内部代码的改编org.squeryl.dsl.QueryDsl._using
:
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()
manualIt.start()
在创建对象时,即在 的构造函数中IterableQuery
,或在对象被传递给控制器之前,可能已经完成了对 的调用。
但是,以这种方式处理资源(文件、数据库连接等)非常脆弱,因为在出现异常时不会触发后处理。如果您查看实现,您会org.squeryl.dsl.QueryDsl._using
看到.try ... finally
IterableQuery