Как я могу включить операцию scala между последовательностью запросов к базе данных в Slick? - PullRequest
0 голосов
/ 29 октября 2018

Допустим, у меня есть модель, которая выглядит примерно так:

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

Если у меня есть соответствующая сопровождающая схема Slick, Users, как я могу выполнить несколько запросов к этой таблице в транзакции с функцией scala, которая вызывается между ними?

Я видел, что Slick предоставляет DBIOAction и DBIO.seq, чтобы разрешить составление нескольких операций с базой данных в одной транзакции, но я не понимаю, могу ли я использовать их с функцией 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)
    )
  }

1 Ответ

0 голосов
/ 29 октября 2018

Общая идея слика заключается в том, чтобы максимально задержать db.run вызов. Предпочтительный способ - работать с DBIOAction экземплярами и объединять их в цепочки, как мы это делаем с scala Future или Option.

Для этого DBIOAction поддерживает map и flatMap методы. DBIOAction сопутствующий объект также содержит вспомогательные методы from, successful и failed. С их помощью вы можете построить DBIOAction из примитивных значений. Для получения дополнительной информации обратитесь к этому разделу гладкой документации о композиции действий.

Можно выполнить все запросы sql в одной транзакции, вызвав транзакционно в DBIOAction экземпляре.

Ваш пример может быть переписан как:

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
)
...