【问题标题】:Scala Slick, how to create Schema ONLY if it does not existScala Slick,仅在不存在时如何创建模式
【发布时间】:2016-02-29 00:32:04
【问题描述】:

在 Scala Slick 中,可以使用以下内容创建数据库模式:

val schema = coffees.schema ++ suppliers.schema
db.run(DBIO.seq(
  schema.create
))

来自本文档页面底部http://slick.typesafe.com/doc/3.0.0/schemas.html

但是,如果数据库架构已经存在,那么这会引发异常。

是否有正常的方式或正确的方式来创建架构,如果且仅当架构不存在时?

【问题讨论】:

    标签: scala slick


    【解决方案1】:

    在 Slick 3.3.0 中添加了 createIfNotExistsdropIfExists 架构方法。所以:

    db.run(coffees.schema.createIfNotExists)
    

    在 Google 上搜索了这个问题,并尝试了几种解决方案,直到弄明白为止。

    【讨论】:

    • 谢谢,这完美地回答了这个问题,应该被接受。
    【解决方案2】:

    这是我为多个表所做的,使用 slick 3.1.1 和 Postgres

    import slick.driver.PostgresDriver.api._
    import slick.jdbc.meta.MTable
    import scala.concurrent.Await
    import scala.concurrent.duration.Duration
    import scala.concurrent.ExecutionContext.Implicits.global
    
    val t1 = TableQuery[Table1]
    val t2 = TableQuery[Table2]
    val t3 = TableQuery[Table3]
    val tables = List(t1, t2, t3)
    
    val existing = db.run(MTable.getTables)
    val f = existing.flatMap( v => {
        val names = v.map(mt => mt.name.name)
        val createIfNotExist = tables.filter( table =>
            (!names.contains(table.baseTableRow.tableName))).map(_.schema.create)
        db.run(DBIO.sequence(createIfNotExist))
    })
    Await.result(f, Duration.Inf)
    

    【讨论】:

      【解决方案3】:

      在 Slick 3.0 中,Mtable.getTablesDBAction,所以这样的东西可以工作:

      val coffees = TableQuery[Coffees]
      try {
        Await.result(db.run(DBIO.seq(
          MTable.getTables map (tables => {
            if (!tables.exists(_.name.name == coffees.baseTableRow.tableName))
              coffees.schema.create
          })
        )), Duration.Inf)
      } finally db.close
      

      【讨论】:

      • 我不断收到一个执行上下文错误,如果我导入scala.concurrent.ExecutionContext.Implicits.global,该错误就会消失,但是没有创建表。我该如何解决?
      • 尝试将MTable.getTables map中的map改为flatMap
      【解决方案4】:

      正如JoshSGoman comment 指出的关于answer of Mike-s 的那样,没有创建表。我设法通过稍微修改第一个答案的代码使其工作:

      val coffees = TableQuery[Coffees]
      
      try {
        def createTableIfNotInTables(tables: Vector[MTable]): Future[Unit] = {
          if (!tables.exists(_.name.name == events.baseTableRow.tableName)) {
            db.run(coffees.schema.create)
          } else {
            Future()
          }
        }
      
        val createTableIfNotExist: Future[Unit] = db.run(MTable.getTables).flatMap(createTableIfNotInTables)
      
        Await.result(createTableIfNotExist, Duration.Inf)
      } finally db.close
      

      使用以下导入:

      import slick.jdbc.meta.MTable
      import slick.driver.SQLiteDriver.api._
      
      import scala.concurrent.{Await, Future}
      import scala.concurrent.duration.Duration
      import scala.concurrent.ExecutionContext.Implicits.global
      

      【讨论】:

      • 为什么要声明db.run两次? coffees.schema.create 不应该在这种情况下仍然有效吗?
      【解决方案5】:

      你为什么不简单地检查create之前的存在?

      val schema = coffees.schema ++ suppliers.schema
      db.run(DBIO.seq(
        if (!MTable.getTables.list.exists(_.name.name == MyTable.tableName)){
          schema.create
        }
      ))
      

      【讨论】:

        【解决方案6】:

        不能对由 3 个表组成的架构使用 createIfNotExists,其中一个表具有复合主键。这里,第 3 表具有由第 1 表和第 2 表各自的主键组成的主键。当第二次遇到 .createIfNotExists 时,我在此架构上遇到错误。我在 scala 2.12.8 上使用 slick 3.3.1。

            class UserTable(tag: Tag) extends Table[User](tag, "user") {
              def id    = column[Long]("id", O.AutoInc, O.PrimaryKey)
              def name  = column[String]("name")
              def email = column[Option[String]]("email")
        
              def * = (id.?, name, email).mapTo[User]
            }
            val users = TableQuery[UserTable]
            lazy val insertUser = users returning users.map(_.id)
        
            case class Room(title: String, id: Long = 0L)
            class RoomTable(tag: Tag) extends Table[Room](tag, "room") {
             def id    = column[Long]("id", O.PrimaryKey, O.AutoInc)
             def title = column[String]("title")
             def * = (title, id).mapTo[Room]
            }
            val rooms = TableQuery[RoomTable]
            lazy val insertRoom = rooms returning rooms.map(_.id)
        
            case class Occupant(roomId: Long, userId: Long)
            class OccupantTable(tag: Tag) extends Table[Occupant](tag, "occupant") {
              def roomId = column[Long]("room")
              def userId = column[Long]("user")
        
              def pk = primaryKey("room_user_pk", (roomId, userId) )
        
              def * = (roomId, userId).mapTo[Occupant]
            }
            val occupants = TableQuery[OccupantTable]
        

        我可以成功创建架构并首先添加用户、房间和居住者。在 .createIfNotExists 的第二次使用中,如下所示,我收到重复主键错误:

          println("\n2nd run on .createIfNotExists using different values for users, rooms and occupants")
          val initdup = for {
            _         <- users.schema.createIfNotExists
            _         <- rooms.schema.createIfNotExists
            _         <- occupants.schema.createIfNotExists
              curlyId   <- insertUser += User(None, "Curly", Some("curly@example.org"))
              larryId   <- insertUser += User(None, "Larry")
              moeId     <- insertUser += User(None, "Moe", Some("moe@example.org"))
              shedId   <- insertRoom += Room("Shed")
              _         <- occupants += Occupant(shedId, curlyId)
              _         <- occupants += Occupant(shedId, moeId)
            } yield ()
        

        例外情况如下:

        2nd run on .createIfNotExists using different values for users, rooms and occupants
        [error] (run-main-2) org.h2.jdbc.JdbcSQLException: Constraint "room_user_pk" already exists; SQL statement:
        [error] alter table "occupant" add constraint "room_user_pk" primary key("room","user") [90045-197]
        [error] org.h2.jdbc.JdbcSQLException: Constraint "room_user_pk" already exists; SQL statement:
        [error] alter table "occupant" add constraint "room_user_pk" primary key("room","user") [90045-197]
        [error]         at org.h2.message.DbException.getJdbcSQLException(DbException.java:357)
        [error]         at org.h2.message.DbException.get(DbException.java:179)
        [error]         at org.h2.message.DbException.get(DbException.java:155)
        [error]         at org.h2.command.ddl.AlterTableAddConstraint.tryUpdate(AlterTableAddConstraint.java:110)
        [error]         at org.h2.command.ddl.AlterTableAddConstraint.update(AlterTableAddConstraint.java:78)
        [error]         at org.h2.command.CommandContainer.update(CommandContainer.java:102)
        [error]         at org.h2.command.Command.executeUpdate(Command.java:261)
        [error]         at org.h2.jdbc.JdbcPreparedStatement.execute(JdbcPreparedStatement.java:249)
        [error]         at slick.jdbc.JdbcActionComponent$SchemaActionExtensionMethodsImpl$$anon$6.$anonfun$run$7(JdbcActionComponent.scala:292)
        [error]         at slick.jdbc.JdbcActionComponent$SchemaActionExtensionMethodsImpl$$anon$6.$anonfun$run$7$adapted(JdbcActionComponent.scala:292)
        [error]         at slick.jdbc.JdbcBackend$SessionDef.withPreparedStatement(JdbcBackend.scala:425)
        [error]         at slick.jdbc.JdbcBackend$SessionDef.withPreparedStatement$(JdbcBackend.scala:420)
        [error]         at slick.jdbc.JdbcBackend$BaseSession.withPreparedStatement(JdbcBackend.scala:489)
        [error]         at slick.jdbc.JdbcActionComponent$SchemaActionExtensionMethodsImpl$$anon$6.$anonfun$run$6(JdbcActionComponent.scala:292)
        [error]         at slick.jdbc.JdbcActionComponent$SchemaActionExtensionMethodsImpl$$anon$6.$anonfun$run$6$adapted(JdbcActionComponent.scala:292)
        [error]         at scala.collection.Iterator.foreach(Iterator.scala:941)
        [error]         at scala.collection.Iterator.foreach$(Iterator.scala:941)
        [error]         at scala.collection.AbstractIterator.foreach(Iterator.scala:1429)
        [error]         at scala.collection.IterableLike.foreach(IterableLike.scala:74)
        [error]         at scala.collection.IterableLike.foreach$(IterableLike.scala:73)
        [error]         at scala.collection.AbstractIterable.foreach(Iterable.scala:56)
        [error]         at slick.jdbc.JdbcActionComponent$SchemaActionExtensionMethodsImpl$$anon$6.run(JdbcActionComponent.scala:292)
        [error]         at slick.jdbc.JdbcActionComponent$SchemaActionExtensionMethodsImpl$$anon$6.run(JdbcActionComponent.scala:290)
        [error]         at slick.jdbc.JdbcActionComponent$SimpleJdbcProfileAction.run(JdbcActionComponent.scala:28)
        [error]         at slick.jdbc.JdbcActionComponent$SimpleJdbcProfileAction.run(JdbcActionComponent.scala:25)
        [error]         at slick.basic.BasicBackend$DatabaseDef$$anon$3.liftedTree1$1(BasicBackend.scala:276)
        [error]         at slick.basic.BasicBackend$DatabaseDef$$anon$3.run(BasicBackend.scala:276)
        [error]         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        [error]         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        [error]         at java.lang.Thread.run(Thread.java:748)
        [error] Nonzero exit code: 1
        [error] (Compile / run) Nonzero exit code: 1
        

        此外,我可以在使用 O.PrimaryKey 约定创建所有表的架构上多次使用 .createIfNotExists。

        我可以做些什么来按摩代码吗?是否有解决方法使 .createIfNotExists 在复合主键情况下仍然可用?

        【讨论】:

        • 我注意到删除“_
        • 您是否建议当前选择的答案是错误的,我应该换一个不同的答案?
        • 对您的问题给出的答案是正确的。 @ДмитрийНикифоров 正确建议在一对多数据库设计问题中使用 createIfNotExists 来处理预先存在的表。这将适用于您的流畅代码。抱歉,如果我引入了任何混淆。我有不同的问题——多对多数据库设计中的预先存在的表——不能用 createIfNotExists 解决。而且,我将发布一个新问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-03
        • 1970-01-01
        • 2016-09-25
        • 2020-12-13
        • 1970-01-01
        • 2020-04-13
        • 2020-08-24
        相关资源
        最近更新 更多