Scala Slick。交易插入批处理

时间:2017-08-31 08:50:56

标签: scala transactions slick

我已经拥有单个实体的create方法:

override def createEntity(entity: Entity) =
  db.run(
    ( for {
      existing <- Entity.filter(e => e.id === entity.id).result  //Check, if entity exists
      e <- if (existing.isEmpty)
             (Entity returning Entity) += Entity(entity.id, entity.name)
           else {
             throw new DuplicateException(s"Create failed: entity already exists")
          }
        } yield e ).transactionally
      )

如何重复使用此方法以事务方式创建实体列表?

//Doesn't work
override def createEntities(entities : List[model.Entity]) = {
    db.run(
      ( for {
          e <- entities
        }
        yield createEntity(e)
      ).transactionally
    )
}

我是slick的新手:(

P.S。抱歉我的英文。

2 个答案:

答案 0 :(得分:1)

问题的根源在于您尝试从内部调用的代码执行多个内部 db.run(在createEntity中声明)外 db.run(在createEntities中声明)。这些外部内部 db.run在概念上是不相关的,因此不会在同一事务中执行。

有点透视

为了更好地了解正在发生的事情以及如何处理这样的情况,我们不得不谈论monad,特别是DBIOAction(我将尝试从概念上解释发生了什么事情。简化和削减一些角落,因为严格的解释将花费太多时间而不是与解决方案相关)。

考虑DBIOAction的一种方式是作为一个步骤(代码)的固定列表,解释如何执行查询并从db获取结果。 db.run实际执行此列表。

需要注意的重要一点是,此列表包含要在数据库中执行的步骤(代码)(SQL查询)以及本地jvm(运行时scala函数对象)。

此外,此列表(代码段)中的每个元素都取决于先前通过从先前输入或通过先前生成的输入。这一切都由一系列map / flatMapsfor表达式粘合在一起)粘合在一起。这看起来像这样:

(1) sql code (generates input for (2))
(2) jvm code (gets input from (1), and generates (3))
(3) sql code (generates input for (4))
(4) jvm code (gets input from (3), and generates (5))
...

请注意,普通的jvm代码可以编织到此列表中,只要它为下一步生成指令(或在代码传递给map时传递最终结果),就可以是任何内容。

这使得monad一般(因此DBIOAction)具有巨大的表达能力,因为这样可以实现整个列表的动态行为(即每个&#34; jvm代码&#34;步骤可以影响未来的计算)。

你可以在&#34; jvm code&#34;中做任何事情的副作用步骤是你也可以产生并执行新的不相关的计算列表(这是你想要做的),这可能没什么问题,但如果你不考虑monadic组合,也会让人感到困惑。

那么,您实际可以做些什么来解决问题?

如果您关注代码重用,我建议您摆脱内部 db.run并提取DBIOAction以后可以{\ n} { db.run中的{1}}和createEntity中的(有些调整)。

您应该能够将代码重写为与此类似的东西(我没有您的确切版本的实体,因此将其视为伪代码):

createEntities

请注意 def createQuery(entity: Entity) = ( for { existing <- Entity.filter(e => e.id === entity.id).result e <- if (existing.isEmpty) (Entity returning Entity) += Entity(entity.id, entity.name) else { throw new DuplicateException(s"Create failed: entity already exists") } } yield e ) def createEntity(entity: Entity) = db.run(createQuery(entity)) def createEntities(entities : List[model.Entity]) = { db.run(DBIO.sequence(entities.map(createQuery(_))).transactionally) } 组合符应用于DBIO.sequence列表并将DBIOAction应用于实际transactionally之前的结果。

<强>旁注

如果您可以控制您的架构,我建议您移动强制执行&#34;唯一ID&#34;从代码中的实体创建到数据库的约束。

答案 1 :(得分:0)

我想你正在寻找类似的东西: http://slick.lightbend.com/doc/3.2.0/dbio.html#sequential-execution

我没有测试代码,我不习惯光滑3.x,但我认为它应该是这样的:

override def createEntities(entities : List[model.Entity]) = {
  db.run(DBIO.seq(entities.map(createEntity):_*).transactionally)
}