每个请求播放2个事务

时间:2014-06-07 11:08:51

标签: sql scala transactions anorm playframework-2.3

对我来说,最方便的事务管理是对整个http请求使用一个可选事务。这意味着第一个SQL语句应该从池中检索连接,启动事务,并在请求处理完成后,应该提交事务(如果抛出异常,则回滚),并且应该关闭连接。当然,如果需要,必须有更细粒度的交易管理。

Play 2是否支持开箱即用?我可能自己可以实现它,但我正在寻找现成的解决方案。

我查看了DB对象,但似乎DB.withConnection每次都使用新的连接(和事务)。

我使用Scala和Anorm db库,如果重要的话。

1 个答案:

答案 0 :(得分:0)

DB.withTransaction是您想要的。就像DB.withConnection一样,它将从连接池为所有包含的SQL函数提供单个连接。由于您希望每个请求使用一个事务,因此最好在控制器函数中调用它,并要求所有模型函数都具有隐式连接参数。

型号:

object Product {

    def checkInventory(id: Long)(implicit c: Connection): Int = SQL(...)

    def decrementInventory(id: Long, quantity: Int)(implicit c: Connection): Boolean = SQL(...)

}

object Cart {

    def addItem(id: Long, quantity: Int)(implicit c: Connection): Boolean = SQL(...)

}

控制器功能:

def addToCart(id: Long, quantity: Int) = Action {

    DB.withTransaction{ implicit connection =>
       if(Product.checkInventory(id) >= quantity && Product.decrementInventory(id, quantity)) {

        Cart.addItem(id, quantity)
        ....
       } else {
           BadRequest
       }
    }
}

免责声明:这显然不是一个逻辑上合理的购物车交易,只是使用数据库交易的简单说明。

通过documentation中的Action个撰写示例,您可以进行特殊的Transaction操作,自动为每个请求配置事务:

object Transaction extends ActionBuilder[Request] {
    def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
        DB.withTransaction{implicit connection => block(request)}
    }
}

使用它就像Action

一样
def addToCart(id: Long, quantity: Int) = Transaction {
    Product.checkInventory(id)

    ...
}

陷阱:以这种方式为每个请求提供一个事务很方便,特别是当大多数控制器函数应该代表原子操作时。但是,在将Connection返回给用户之前,此方法不会从事务中释放Result。这意味着如果您要返回一个需要很长时间为客户端提供服务/呈现的大型数据集,那么您将保持连接的时间比实际需要的时间长得多。