【发布时间】:2016-11-08 07:53:35
【问题描述】:
我知道 scala 作为一种函数式语言,其工作方式应该与常见的 OO 语言(如 Java)不同,但我确信必须有一种方法可以将一组数据库更改包装在单个事务中,确保原子性以及所有其他 ACID 属性。
如 slick 文档 (http://slick.lightbend.com/doc/3.1.0/dbio.html) 中所述,DBIOAction 允许在事务中对数据库操作进行分组,如下所示:
val a = (for {
ns <- coffees.filter(_.name.startsWith("ESPRESSO")).map(_.name).result
_ <- DBIO.seq(ns.map(n => coffees.filter(_.name === n).delete): _*)
} yield ()).transactionally
val f: Future[Unit] = db.run(a)
但是,我的用例(以及我能想到的大多数真实示例),我有一个带有控制器的代码结构,它公开了我的 REST 端点的代码,该控制器调用多个服务,每个服务将委托数据库操作到 DAO。
我常用代码结构的粗略示例:
class UserController @Inject(userService: UserService) {
def register(userData: UserData) = {
userService.save(userData).map(result => Ok(result))
}
}
class UserService @Inject(userDao: UserDao, addressDao: AddressDao) {
def save(userData: UserData) = {
for {
savedUser <- userDao.save(userData.toUser)
savedAddress <- addressDao.save(userData.addressData.toAddress)
} yield savedUser.copy(address = savedAddress)
}
}
class SlickUserDao {
def save(user: User) = {
db.run((UserSchema.users returning UserSchema.users)).insertOrUpdate(user)
}
}
这是一个简单的例子,但大多数在服务层都有更复杂的业务逻辑。
我不想要:
- 我的 DAO 拥有业务逻辑并决定运行哪些数据库操作。
- 从我的 DAO 返回 DBAction 并公开持久性类。这完全违背了使用 DAO 的初衷,并使进一步的重构变得更加困难。
但我绝对想要一个围绕我的整个控制器的事务,以确保如果任何代码失败,则在执行该方法时所做的所有更改都将回滚。
如何在 Scala Play 应用程序中使用 Slick 实现完整的控制器事务性?我似乎找不到任何有关如何执行此操作的文档。
另外,如何在 slick 中禁用自动提交?我确定有办法,但我只是错过了一些东西。
编辑:
所以阅读更多关于它的内容,我觉得现在我更好地理解了 slick 如何使用与数据库和会话的连接。这很有帮助:http://tastefulcode.com/2015/03/19/modern-database-access-scala-slick/。
我正在做的是一个在期货中作曲的案例,根据这篇文章,没有办法将相同的连接和会话用于这种类型的多个操作。
问题是:我真的不能使用任何其他类型的构图。我有相当多的业务逻辑需要在查询之间执行。
我想我可以更改我的代码以允许我使用动作组合,但正如我之前提到的,这迫使我在编写业务逻辑时考虑到事务性等方面。那不应该发生。它污染了业务代码,并且使编写测试变得更加困难。
有没有解决这个问题的方法?有什么 git 项目可以解决我错过的问题吗?或者,更激烈的是,任何其他支持这一点的持久性框架?根据我的阅读,Anorm 很好地支持这一点,但我可能会误解它并且不想更改框架以发现它不支持(就像 Slick 发生的那样)。
【问题讨论】:
标签: scala playframework playframework-2.0 slick slick-3.0