好的 - 让我们将您的问题分解为三个部分。
的 如何在事务中执行块 强>
基本上看了这个答案: 如何在光滑中使用交易
一旦你转换 DBIO 至 Future 你完成了没有机会在单个交易中组成多个操作。故事结局。
DBIO
Future
的 如何避免使用 Slick 在测试中 强>
Slick
这基本上是一个设计问题 - 如果你想拥有一个业务层 Repository / DAO / what - 而不是让这个服务层处理事务。您不需要与之互动 Slick 在这一层之外。
Repository
DAO
的 避免依赖于您的存储库接口 Slick 强>
以最简单的方式 - 你需要依赖Slick DBIO 在交易中撰写作业(和作曲 Repository 事务中的方法是在任何严肃的应用程序中都无法避免的。
如果你想避免依赖 DBIO 你可能会创建自己的monadic类型,比方说 TransactionBoundary[T] 要么 TransactionContext[T] 。
TransactionBoundary[T]
TransactionContext[T]
然后你会有类似的东西 TransactionManager 那会执行这个 TransactionContext[T] 。
TransactionManager
恕我直言不值得努力,我只是使用 DBIO 它已经有一个辉煌的名字(像Haskell的 IO monad - DBIO 告诉你你有描述 IO 在您的存储上执行的操作)。但我们假设你仍然想避免它。
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 。
TransactionBoundary
但是再次 - 恕我直言,上述所有内容并没有带来任何实际优势 DBIO (除了美学的味道)。如果你想避免使用 Slick 某些图层之外的相关类,只需创建一个这样的类型别名:
type TransactionBoundary[T] = DBIO[T]
并在任何地方使用它。