Выполнение действий вне базы данных в транзакции в Slick 3

У меня возникли проблемы с пониманием нового Slick DBIOAction API, для которого, похоже, не так много примеров в документации. Я использую Slick 3.0.0, и мне нужно выполнить некоторые действия с БД, а также некоторые вычисления с данными, полученными из базы данных, но все эти действия должны быть выполнены внутри одной транзакции. Я пытаюсь сделать следующее:

  1. Выполнить запрос к базе данных (таблица types).
  2. Выполните некоторую агрегацию и фильтрацию результатов запроса (этот расчет нельзя выполнить в базе данных).
  3. Выполните другой запрос, основанный на вычислениях из шага 2 (таблица messages — из-за некоторых ограничений этот запрос должен быть в чистом SQL).
  4. Соедините данные из шага 2 и 3 в памяти.

Я хочу, чтобы запросы из шагов 1 и 3 выполнялись внутри транзакции, так как данные из их наборов результатов должны быть согласованы.

Я пытался сделать это в монадическом стиле соединения. Вот слишком упрощенная версия моего кода, но я даже не могу его скомпилировать:

  val compositeAction = (for {
    rawTypes <- TableQuery[DBType].result
    (projectId, types) <- rawTypes.groupBy(_.projectId).toSeq.map(group => (group._1, group._2.slice(0, 10)))
    counts <- DBIO.sequence(types.map(aType => sql"""select count(*) from messages where type_id = ${aType.id}""".as[Int]))
  } yield (projectId, types.zip(counts))).transactionally
  1. Первая строка понимания for выбирает данные из таблицы types.
  2. Предполагается, что вторая строка for понимания выполняет некоторую группировку и нарезку результатов, в результате чего получается Seq[(Option[String], Seq[String])]
  3. Третья строка понимания for должна выполнить набор запросов для каждого элемента из предыдущего шага, в частности, она должна выполнить один SQL-запрос для каждого из значений внутри Seq[String]. Итак, в третьей строке я строю последовательность из DBIOActions.
  4. Пункт yield zips types из второго шага и counts из третьего шага.

Эта конструкция, однако, не работает и дает две ошибки времени компиляции:

Error:(129, 16) type mismatch;
 found   : slick.dbio.DBIOAction[(Option[String], Seq[(com.centreit.proto.repiso.storage.db.models.DBType#TableElementType, Vector[Int])]),slick.dbio.NoStream,slick.dbio.Effect]
    (which expands to)  slick.dbio.DBIOAction[(Option[String], Seq[(com.centreit.proto.repiso.storage.db.models.TypeModel, Vector[Int])]),slick.dbio.NoStream,slick.dbio.Effect]
 required: scala.collection.GenTraversableOnce[?]
        counts <- DBIO.sequence(types.map(aType => sql"""select count(*) from messages where type_id = ${aType.id}""".as[Int]))
               ^
Error:(128, 28) type mismatch;
 found   : Seq[Nothing]
 required: slick.dbio.DBIOAction[?,?,?]
        (projectId, types) <- rawTypes.groupBy(_.projectId).toSeq.map(group => (group._1, group._2.slice(0, 10)))
                           ^

Я попытался обернуть вторую строку в DBIOAction, используя DBIO.successful, который должен поднимать постоянное значение в монаду DBIOAction:

(projectId, types) <- DBIO.successful(rawTypes.groupBy(_.projectId).toSeq.map(group => (group._1, group._2.slice(0, 10))))

Но в этом коде переменная types подразумевается как Any, и из-за этого код не компилируется.


person Sergei    schedule 28.06.2015    source источник
comment
Взгляните на этот пример и посмотрите, поможет ли он вам.   -  person Felipe    schedule 28.06.2015
comment
@FelipeAlmeida Спасибо, но это не совсем то, что мне нужно. В этом примере все включения являются обновлениями/удалениями/вставками, и они не связаны между собой своими результатами и ничего не дают (все левые части _). В моем случае одно из моих действий вообще не имеет ничего общего с базой данных, и все действия возвращают результаты, которые используются в других включениях или в yield.   -  person Sergei    schedule 29.06.2015


Ответы (1)


Попробуйте так:

val compositeAction = (for {
  rawTypes <- TableQuery[DBType].result
  pair <- DBIO.sequence(rawTypes.groupBy(_.projectId).toSeq.map(group => DBIO.successful(group)))
  counts <- DBIO.sequence(pair.head._2.map(aType => sql"""select count(*) from messages where type_id = ${aType.id}""".as[Int]))
} yield (pair.head._1, pair.head._2.zip(counts))).transactionally
person Bhavya Latha Bandaru    schedule 02.07.2015
comment
Спасибо! Кажется, это то, что я искал. - person Sergei; 08.07.2015