好的 - 让我们将您的问题分解为三个部分。
如何在交易中执行区块
基本上读了这个答案:如何在slick中使用事务 https://stackoverflow.com/questions/41219200/how-to-use-transaction-in-slick/41219465#41219465
一旦您转换DBIO
to Future
你完成了。没有机会在单个事务中组成多个操作。故事结局。
如何避免使用Slick
在测试中
这基本上是一个设计问题 - 如果你想在之上有一个业务层Repository
/ DAO
/ 无论如何 - 让这个服务层处理事务。您无需与Slick
在这一层之外。
避免依赖存储库接口Slick
以最直接的方式 - 您需要依赖 SlickDBIO
在事务中组合操作(并组合Repository
事务中的方法是任何严肃的应用程序中都无法避免的)。
如果你想避免依赖于DBIO
你也许会创建你自己的一元类型,比如说TransactionBoundary[T]
or TransactionContext[T]
.
然后你会得到类似的东西TransactionManager
这将执行这个TransactionContext[T]
.
恕我直言,不值得付出努力,我只是使用DBIO
它已经有了一个辉煌的名字(就像 Haskell 的IO
单子-DBIO
通知您您有以下描述IO
对您的存储执行的操作)。但我们假设您仍然想避免它。
你也许可以做类似的事情:
package transaction {
object Transactions {
implicit class TransactionBoundary[T](private[transaction] val dbio: DBIO[T]) {
// ...
}
}
class TransactionManager {
def execute[T](boundary: TransactionBoundary[T]): Future[T] = db.run(boundary.dbio)
}
}
你的特质看起来像这样:
trait UserRepository {
findById(id: Long): TransactionBoundary[Option[User]]
save(user: User): TransactionBoundary[Unit]
}
在你的代码中的某个地方你会这样做:
transactionManager.execute(
for {
userResult <- userRepository.save(user)
memberRepository.save(member)
} yield ()
)
通过使用隐式转换,您将获得方法的结果Repository
自动转换为您的TransactionBoundary
.
但同样 - 恕我直言,以上所有内容并没有比使用带来任何实际优势DBIO
(也许美学品味除外)。如果你想避免使用Slick
特定层之外的相关类,只需创建一个类型别名,如下所示:
type TransactionBoundary[T] = DBIO[T]
并在任何地方使用它。