如何在 Slick 中的一系列数据库查询之间包含 scala 操作?

How can I include a scala operation between a sequence of database queries in Slick?

假设我有一个看起来像这样的模型:

case class User(username: String, dateOfBirth: Timestamp, lastSentGift: Timestamp)

假设我有一个合适的伴随 Slick 模式,Users,我如何在一个事务中针对这个 table 执行多个查询,并在其间调用一个 scala 函数?

我看到 Slick 提供了 DBIOActionDBIO.seq 允许在单个事务中组合多个数据库操作,但我不明白 if/how 我可以使用这些中间调用了一个 scala 函数。

例如,我想做这样的事情,但将所有内容都保存在一个事务中:

def prepareGiftFor(user: User): Timestamp = ???

val usersWithBirthdays = db.run(
  Users.filter { user => 
    user.dateOfBirth > (now - 1 month) && user.lastSentGift < (now - 1 month)
  }
  .limit(100)
  .forUpdate
)

usersWithBirthdays
  .map(user => (user.username, prepareGiftFor(user)))
  .map { case (username, lastSentGift) =>
    db.run(
      Users.withFilter(_.username === username)
        .map(row => row.lastSentGift)
        .update(lastSentGift)
    )
  }

slick 的总体思路是尽可能延迟 db.run 调用。首选方法是使用 DBIOAction 个实例并将它们链接起来,就像我们使用 scala FutureOption 一样。

为此 DBIOAction 支持 mapflatMap 方法。 DBIOAction companion object also contains helper methods from, successful and failed. With them you can construct DBIOAction from primitive values. For more information check this section of slick documentation关于动作组合。

通过在 DBIOAction 个实例上调用 transactionally,可以在单个事务中 运行 所有 sql 查询。

您的示例可以重写为:

def prepareGiftFor(user: User): Timestamp = ???

def findUsersWithBirthdays(): DBIO[Seq[User]] = {
  Users
    .filter { user =>
      user.dateOfBirth > (now - 1 month) && user.lastSentGift < (now - 1 month)
    }
    .limit(100)
    .forUpdate
}

def updateUsers(users: Seq[User]): Seq[DBIO[Int]] = {
  users
    .map(user => (user.username, prepareGiftFor(user)))
    .map {
      Users
        .withFilter(_.username === username)
        .map(row => row.lastSentGift)
        .update(lastSentGift)
    }
}

db.run(
  (for {
    users <- findUsersWithBirthdays()
    _ <- DBIO.sequience(updateUsers(users))
  } yield ()).transactionaly
)