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