使用Akka& Scala进行Scala光滑删除查询Postgres的

时间:2014-12-22 02:09:53

标签: scala akka slick-2.0

我正在尝试编写一种方法,它只是根据id从数据库中删除一行。

class PolicyHolderDAO(database: DatabaseDef) extends CRUDActor[PolicyHolder] {

  private val policyHolders: TableQuery[PolicyHolderTable] = TableQuery[PolicyHolderTable]
  implicit val system: ActorSystem = ActorSystem("Bitcoin-Insurance")
  import system.dispatcher
  implicit val timeout: Timeout = Timeout(5.seconds)
  private implicit var session = database.createSession

  override def receive = {
    case PolicyHolderDAO.Read(id) => sender ! read(id)
    case PolicyHolderDAO.Create(policyHolder) => sender ! create(policyHolder)
    case PolicyHolderDAO.Delete(policyHolder) => sender ! delete(policyHolder)
  }

  /**
   * @param policyHolder the policy holder to inserted into the database
   * @return id
   */
  override def create(policyHolder: PolicyHolder): Future[PolicyHolder] = {
    future {
      (policyHolders returning policyHolders.map(_.id) into
        ((policyHolder, id) => policyHolder.copy(id = Some(id)))) += policyHolder

    }
  }
  /**
   * @param id the id that corresponds to a policy holder
   * @return a future value of a policy holder if the policy holder exists in the database, else it returns none
   */
  override def read(id: Future[Long]): Future[Option[PolicyHolder]] = {
    id.map(i => policyHolders.filter(p => p.id === i).firstOption)
  }

  /**
   * @param policyHolder the policyHolder to be updated
   * @return policyHolder the policyHolders information now saved in the database
   */
  override def update(policyHolder: Future[PolicyHolder]): Future[Option[PolicyHolder]] = {
    /*  //val policyHolderFromDb = policyHolder.map(p => policyHolders.filter(_.id === p.id.getOrElse(-1)))
    val policyHolderFromDb = for (p <- policyHolder;  q = policyHolders.filter(_.id === p.id) ) yield q.update

    val updatedPolicyHolder: Future[Option[PolicyHolder]] = for (p <- policyHolderFromDb; result = create(p)) yield result
    updatedPolicyHolder*/
    Future(None)
  }
  /**
   * @param policyHolder the policy holder to be deleted from our database
   * @return affectedRows the number of rows effected by this query
   */
  override def delete(policyHolder: Future[PolicyHolder]): Future[Int] = {
    val policyHolderId: Future[Long] = policyHolder.map(p => p.id.getOrElse(-1))
    val affectedRows = for (id <- policyHolderId; q = policyHolders.filter(_.id === id)) yield q
    affectedRows.map(q => q.delete)

  }
}

我正在尝试使用为Scala测试编写的测试用例来测试它

  "A PolicyHolderDAO Actor" must {
    "be able to delete an existing policy holder from our database" in {
      val policyHolder = PolicyHolder(None, "Chris", "Stewart")
      val createdPolicyHolderAny: Future[Any] = policyHolderDAOActor ? PolicyHolderDAO.Create(policyHolder)
      val createdPolicyHolder: Future[PolicyHolder] = createdPolicyHolderAny.mapTo[Future[PolicyHolder]].flatMap(p => p)

      policyHolderDAOActor ! PolicyHolderDAO.Delete(createdPolicyHolder)

      val deletedPolicyHolderAny: Future[Any] = policyHolderDAOActor ? PolicyHolderDAO.Get(createdPolicyHolder.map(_.id))
      val deletedPolicyHolder: Future[Option[PolicyHolder]] = deletedPolicyHolderAny.mapTo[Future[Option[PolicyHolder]]].flatMap(p => p)
      whenReady(deletedPolicyHolder, timeout(10 seconds), interval(5 millis)) { p =>
        val policyHolderExists = p match {
          case Some(a) =>
            println(a)
            true
          case None => false
        }
        policyHolderExists must be(false)
      }
    }
  }

但是我没有通过这个测试。原因是该行未从我们的数据库中删除。我不确定为什么这个测试失败了。我有一个相应的单元测试这个方法,由于删除返回1个受影响的行,这是有道理的。 Akka / Futures可能会有一些我不理解的东西吗?

谢谢!

3 个答案:

答案 0 :(得分:1)

您的测试中可能存在竞争条件:

...
policyHolderDAOActor ! PolicyHolderDAO.Delete(createdPolicyHolder)

val deletedPolicyHolderAny: Future[Any] = policyHolderDAOActor ? PolicyHolderDAO.Get(createdPolicyHolder.map(_.id))
...

很难说,因为你没有公开Actor这样做,但基于上面的delete方法返回Future[Int]的事实,似乎正在发生的事情是:

  • policyHolderDAOActor在tell中传递删除消息。
  • 然后立即要求
  • policyHolderDAOActor按ID检索相同的记录。
  • policyHolderDAOActor调用delete方法,该方法返回Future[Int],因此结果可能会被丢弃,receive会立即返回。
  • policyHolderDAOActor然后可以自由处理下一条消息以按ID获取记录,但删除尚未完成处理,因此记录仍在那里。

在上面两行之间引入等待可能使测试通过,但如果没有,或许共享一些Actor代码会更有说服力。

如果其他消息的功能类似,那么当您尝试删除记录时,甚至还没有创建记录。上面的子弹仍然是正确的,只是采用不同的方法。为了安全起见,您应该阻止测试中的每个异步操作,以确保顺序处理。

答案 1 :(得分:0)

我认为您的问题在于将Slick会话传递到正确的位置:

  1. delete没有包含(implicit val session: Session)
  2. 的第二个参数列表
  3. 当演员准备好时,会话可能已经关闭。因此,您需要明确地获得一个新的。
  4. 我不确定1),因为我习惯使用Slick with Play,这可能会有一些不同的API,但我有一些经验2):

    https://github.com/payola/payola-viz/blob/b5668bcbdefc3bce7e6095d211fa04c2aa697685/src/app/model/services/LDVMServiceImpl.scala#L44

答案 2 :(得分:0)

只是一个侧面评论,为什么不让delete更简单?

def delete(policyHolder: Future[PolicyHolder]): Future[Int] = {
   for (p <- policyHolder) yield policyHolders.delete(_.id === p.id)
}