scala - Squeryl:显式运行查询

标签 scala orm playframework playframework-2.0 squeryl

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

围绕查询的执行必须有一个 transaction{} 或一个 inTransaction{} block 。

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

我想在我的应用程序模型中创建一个查询,并将其直接传递给模板中的 View 助手对其进行迭代并呈现数据的 View 。
这只有在将 transaction{} block 放入 Controller 时才有可能( Controller 包含模板的调用,因此执行迭代的模板也在内部)。不可能将 transaction{} block 放入模型中,因为模型并没有真正执行查询。

但据我了解,交易与 Controller 无关。由模型决定使用哪个数据库框架、如何使用它以及在哪里使用事务。所以我希望 transaction{} block 在模型中。

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

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

最佳答案

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

  • 数据的惰性计算;这里:数据库结果
  • 隐藏对必须在计算完成时触发的后处理操作的需求;此处:从您的 Controller 中隐藏或查看必须关闭数据库 session

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

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

    也就是说,我能想到的最好的就是以下,这是对 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 的构造函数内,或者在对象被传递给 Controller ​​之前。

    但是,以这种方式处理资源(文件、数据库连接等)非常脆弱,因为在出​​现异常时不会触发后处理。如果你看一下 org.squeryl.dsl.QueryDsl._using 的实现你会看到几个 try ... finally IterableQuery 中缺少的 block .

    关于scala - Squeryl:显式运行查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11067425/

    相关文章:

    scala - Apache Flink:使用keyBy/connect维护流中的消息输入顺序

    sql - 有没有SQL + Redis Hybrid ORM/OHM框架?

    playframework - play 2.3.8怎么设置http地址

    java - Ebean/Play 的 OptimisticLockException

    java - 在 Play Framework 中下载动态创建的 zip 文件

    scala - Yarn 集群不能平等地管理 vcore,超出队列资源限制

    scala - Scala 中是否有任何日期/时间 DSL 实用程序?

    scala - 我应该如何在 spark 文本文件中表达 hdfs 路径?

    java - hibernate 零对一

    python - 我可以让 SQLAlchemy 在不重复完整的原始查询的情况下进行子查询预加载吗?