加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – Slick 3:如何使用事务实现存储库模式?

发布时间:2020-12-16 18:36:53 所属栏目:安全 来源:网络整理
导读:在我的play framework(2.5)app中,我需要为服务编写单元测试. 我需要隔离数据访问逻辑,以便能够单独测试服务层, 为此,我想在我的单元测试中创建存储库接口并将它们MOCK: class UserService { def signUpNewUser(username: String,memberName: String): Futur
在我的play framework(2.5)app中,我需要为服务编写单元测试.

我需要隔离数据访问逻辑,以便能够单独测试服务层,
为此,我想在我的单元测试中创建存储库接口并将它们MOCK:

class UserService {
   def signUpNewUser(username: String,memberName: String): Future[Unit] {
      val userId = 1 // Set to 1 for demo
      val user = User(userId,username)
      val member = Member(memberName,userId) 
      // ---- I NEED TO EXECUTE THIS BLOCK WITHIN TRANSACTION ----
      for {
        userResult <- userRepository.save(user)
        memberRepository.save(member)
      } yield ()     
      // ---- END OF TRANSACTION ----
   }
}

在上面的示例中,userRepository.save(User)和memberRepository.save(member)操作应该在事务中执行.

我不想直接在我的服务层使用光滑,因为它会使我的测试复杂化.

另外,我不想在我的单元测试中使用嵌入式数据库,在其他地方它将是一个NOT单元测试,我需要完全隔离.

我不希望我的存储库接口完全依赖于光滑,但是需要这样的东西:

trait UserRepository {
   findById(id: Long): Future[Option[User]]
   save(user: User): Future[Unit] 
}

如何用光滑实现这一目标?

解决方法

好的 – 让我们将您的问题分解为三个部分.

如何在事务中执行块

基本上读了这个答案:How to use transaction in slick

一旦你将DBIO转换为Future,你就完成了.没有机会在单个交易中组成多个操作.故事结局.

如何避免在测试中使用Slick

这基本上是一个设计问题 – 如果你想在Repository / DAO /之上有一个业务层 – 而不是让这个服务层处理事务.您不需要在此图层之外与Slick交互.

避免存储库接口依赖于Slick

以最简单的方式 – 您需要依赖Slick DBIO来组成事务中的操作(并且在事务中组合Repository方法是在任何严肃的应用程序中都无法避免的).

如果你想避免依赖DBIO,你可能会创建自己的monadic类型,比如TransactionBoundary [T]或TransactionContext [T].

那么你会有类似TransactionManager的东西来执行这个TransactionContext [T].

恕我直言不值得努力,我只是使用DBIO,它已经有一个很棒的名字(比如Haskell的IO monad – 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]

并在任何地方使用它.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读