【问题标题】:Slick Write a Simple Table Creation FunctionSlick 写一个简单的建表函数
【发布时间】:2013-12-20 00:29:22
【问题描述】:

Emm...我正在用 Play 2 试用 Slick。表创建过程变得非常令人沮丧,因为与其他 ORM(如 ebean)不同,Slick 不会检测数据库是否已创建,如果已经存在表存在,会报异常。我只是不想每次重启服务器时都删除和创建,所以我决定写一个小函数来帮助我:

  def databaseCreate(tables: Table*) = {
     for (table <- tables) {
       if (MTable.getTables(table.getClass.getName).list.isEmpty) table.ddl.create
     }
  }

它的作用是接收一些像这样的对象:

  object Tag extends Table [(Option[Int], String)]("Tags") {
    def id = column[Int]("TAG_ID", O.PrimaryKey, O.AutoInc)
    def tag_name = column[String]("TAG_NAME")

    def * = id.? ~ tag_name
  }

并使用scala.slick.jdbc.meta.MTable 中的MTable 方法来了解该表是否存在。然后我遇到了一个简单的 Java 反射问题。如果databaseCreate 方法采用字符串,那么我可以调用.ddl.create。所以我决定传入对象并使用 relfection:table.getClass.getName。唯一的问题是类型不匹配:(来自我的 IDE)

预期:MySQLDriver.simple.type#Table,实际:BlogData.Tag.type

BlogData 是我用来存储所有较小表对象的大对象。我该如何解决这个不匹配问题?使用一大堆asInstanceOf?这会使命令变得难以忍受又长又丑……


更正:

类型不匹配是一个误报,它来自 IDE,而不是来自类型安全控制台编译器。真正的问题是:

type Table takes type parameters
def databaseCreate(tables: Table*) = {
^
one error found

然后我按照建议更改了代码:

def databaseCreate(tables: Table[_]*)(implicit session: Session) = {
         for (table <- tables) {
           if (MTable.getTables(table.tableName).list.isEmpty) table.ddl.create
         }
      }

然后我得到了这个错误:

ambiguous implicit values: both value session of type slick.driver.MySQLDriver.simple.Session and method threadLocalSession in object Database of type => scala.slick.session.Session match expected type scala.slick.session.Session
if (MTable.getTables(table.tableName).list.isEmpty) table.ddl.create
^
one error found

我的导入在这里:

import play.api.GlobalSettings
import play.api.Application
import models.BlogData._
import scala.slick.driver.MySQLDriver.simple._
import Database.threadLocalSession
import play.api.Play.current
import scala.slick.jdbc.meta.MTable

【问题讨论】:

标签: scala playframework slick


【解决方案1】:

在 Slick 2 中:为了完整起见,请参阅 thisthat 相关线程。

我使用以下方法:

def createIfNotExists(tables: TableQuery[_ <: Table[_]]*)(implicit session: Session) {
  tables foreach {table => if(MTable.getTables(table.baseTableRow.tableName).list.isEmpty) table.ddl.create}
}

然后您可以使用隐式会话创建表:

db withSession {
  implicit session =>
    createIfNotExists(table1, table2, ..., tablen)
}

【讨论】:

    【解决方案2】:

    我假设您使用的是 SLICK v1,因为您有 Table 对象。我有 v1 的工作代码。此版本将在重新创建之前删除所有存在的表(但不会删除您不希望重新创建的任何表)。它还会在创建之前将 DDL 合并为一个,以使创建顺序正确:

    def create() = database withSession {
      import scala.slick.jdbc.{StaticQuery => Q}
      val tables = Seq(TableA, TableB, TableC)
      def existingTables = MTable.getTables.list().map(_.name.name)
      tables.filter(t => existingTables.contains(t.tableName)).foreach{t =>
        Q.updateNA(s"drop table ${t.tableName} cascade").execute
      }
      val tablesToCreate = tables.filterNot(t => existingTables.contains(t.tableName))
      val ddl: Option[DDL] = tablesToCreate.foldLeft(None: Option[DDL]){(ddl, table) =>
        ddl match {
          case Some(d) => Some(d ++ table.ddl)
          case _ => Some(table.ddl)
        }
      }
      ddl.foreach{_.create}
    }
    

    【讨论】:

      【解决方案3】:

      不知道为什么您会收到此错误消息。我需要查看您的导入以及您调用databaseCreate 的位置,但问题是def databaseCreate(tables: Table*) 应该是def databaseCreate(tables: Table[_]*),并且可能还需要第二个参数列表def databaseCreate(tables: Table[_]*)(implicit session: Session) = ...

      另外,您可以使用 table.tableName 代替 table.getClass.getName。

      【讨论】:

      • 啊!!谢谢,至少现在第一个错误(来自编译器,而不是愚蠢的 IDE)得到了解决。现在它只是说session 是模棱两可的。
      • 删除 threadLocalSession 导入并在需要会话的地方使用 db.withSession{ session:Session => ... }。或者(但我建议前者)删除(隐式会话:会话)参数列表,我建议您将其添加为不希望您使用 threadLocalSession。
      • 是否有理由不导入threadLocalSession 或者总是使用{session:Session =&gt; ...}?我在其他地方也用过threadLocalSession..
      • 使用db.withSession{ session:Session =&gt; ... } 确保在编译时你有一个会话。当你导入 threadLocalSession 时,你可以忘记 db.withSession{ ... } 调用,Scala 编译器将看不到它。
      • 哇!谢谢!终于解决了这个问题。发生类型不匹配是因为我导入了两个包含相同类的不同源,而您在会话中帮助了我(很重要)!谢谢!我可能不会再使用threadLocalSession了
      【解决方案4】:

      您应该将 asTry 与表 create DBIOAction 一起使用。它将检查是否存在表......如果不存在,DBIOAction 将用于创建表。

      在主页DAO中

         val createHomePageTableAction: DBIOAction[Int, NoStream, Effect.Schema with Effect.Write] = homePageTable.schema.create >>(homePageTable+=Homepage("No Data","No Data",0l))
      

      在 ExplorePageDAO 中

        val SoftwareTableCreateAction: FixedSqlAction[Unit, NoStream, Effect.Schema] = softwareTable.schema.create
      

      在 createTablesController 中

      package controllers
      
      import javax.inject.{Inject, Singleton}
      import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents}
      import services.dbServices.{ExplorePageDAO, HomePageDAO}
      
      import scala.concurrent.ExecutionContext
      
      @Singleton
      class CreateTablesController @Inject()(cc: ControllerComponents)(implicit assetsFinder: AssetsFinder, executionContext: ExecutionContext)
        extends AbstractController(cc) {
      
        import services.dbServices.dbSandBox._
        def createTable: Action[AnyContent] = Action.async {
      
          dbAccess.run(HomePageDAO.createHomePageTableAction .asTry >> ExplorePageDAO.SoftwareTableCreateAction.asTry).map(_=> Ok("Table Created"))
        }
      
      
      }
      

      【讨论】:

        猜你喜欢
        • 2016-04-13
        • 1970-01-01
        • 2023-03-31
        • 2017-04-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多