对我来说最方便的事务管理是对整个 http 请求使用一个可选事务。这意味着第一个 SQL 语句应该从池中检索连接,启动事务,在请求处理完成后,应该提交事务(如果抛出异常,则回滚)并关闭连接。当然,如果需要,更细粒度的事务管理必须是可能的。
Play 2 是否支持开箱即用?我可能可以自己实现它,但我正在寻找现成的解决方案。
我查看了 DB 对象,但似乎 DB.withConnection 每次都使用新的连接(和事务)。
如果重要的话,我正在使用 Scala 和 Anorm db 库。
最佳答案
DB.withTransaction
是你想要的。就像 DB.withConnection
,它将从连接池中为所有包含的 SQL 函数提供一个连接。由于您希望每个请求使用一个事务,因此在 Controller 函数中调用它似乎是最有意义的,并且要求所有模型函数都具有隐式连接参数。
模型:
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(...)
}
Controller 功能:
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
}
}
}
免责声明:这显然不是逻辑上合理的购物车交易,而只是使用数据库交易的简单说明。
关注
Action
documentation 中的组合示例,你可以做个特别的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)
...
}
陷阱 :以这种方式为每个请求提供一个事务很方便,尤其是当您的大多数 Controller 功能都应该表示原子操作时。但是,此方法不会释放
Connection
从交易到Result
已退还给用户。这意味着,如果您要返回一个需要很长时间为客户端提供/呈现的大型数据集,那么您将保持连接的时间比您真正需要的时间长得多。
关于sql - 每个请求播放 2 个事务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24096536/