【问题标题】:Play framework and Slick automatic database creationPlay框架和Slick自动数据库创建
【发布时间】:2016-01-09 03:02:51
【问题描述】:

我用的是play 2.4和Slick 3,是否可以自动生成ddl脚本,是进化的?

在官方文档中我找到了一些脚本,但我应该将它放在游戏框架中的哪个位置? http://slick.typesafe.com/doc/3.1.0/schemas.html

您是否知道任何库来管理代码演变而不是编写普通 SQL?

【问题讨论】:

    标签: scala playframework slick playframework-evolutions


    【解决方案1】:

    我使用 PostgresDriver 做了一些解决方法,我创建了模块,将 DDL 打印到文件。每次代码更改后,我只需要替换 1.sql 或稍后修改下一个进化脚本:

    ComputersDatabaseModule.scala

    package bootstrap
    
    import com.google.inject.AbstractModule
    import play.api.{Mode, Play}
    
    class ComputersDatabaseModule extends AbstractModule {
    
      protected def configure() = {
        bind(classOf[CreateDDL]).asEagerSingleton()
        bind(classOf[InitialData]).asEagerSingleton()
      }
    }
    

    CreateDDL.scala

    package bootstrap
    
    import java.io.PrintWriter
    import javax.inject.Inject
    
    import dao.{CompaniesMapping, ComputersMapping}
    import play.api.db.slick.{HasDatabaseConfigProvider, DatabaseConfigProvider}
    import slick.driver.JdbcProfile
    
    
    /**
      * Creates DDL script
      */
    private[bootstrap] class CreateDDL @Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] with
      ComputersMapping with CompaniesMapping {
    
      def createDDLScript() = {
        import slick.driver.PostgresDriver.api._
    
        val allSchemas = companies.schema ++ computers.schema
    
        val writer = new PrintWriter("target/migration_ddl.sql")
        writer.write("# --- !Ups\n\n")
        allSchemas.createStatements.foreach { s => writer.write(s + ";\n") }
    
        writer.write("\n\n# --- !Downs\n\n")
        allSchemas.dropStatements.foreach { s => writer.write(s + ";\n") }
    
        writer.close()
      }
    
      createDDLScript()
    }
    

    ComputersDAO.scala

    package dao
    
    import java.util.Date
    import javax.inject.{Inject, Singleton}
    import models.{Company, Computer, Page}
    import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
    import play.api.libs.concurrent.Execution.Implicits.defaultContext
    import slick.driver.JdbcProfile
    
    import scala.concurrent.Future
    
    trait ComputersMapping { self: HasDatabaseConfigProvider[JdbcProfile] =>
      import driver.api._
    
      class Computers(tag: Tag) extends Table[Computer](tag, "COMPUTER") {
    
        implicit val dateColumnType = MappedColumnType.base[Date, Long](d => d.getTime, d => new Date(d))
    
        def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
        def name = column[String]("NAME")
        def introduced = column[Option[Date]]("INTRODUCED")
        def discontinued = column[Option[Date]]("DISCONTINUED")
        def companyId = column[Option[Long]]("COMPANY_ID")
    
        def * = (id.?, name, introduced, discontinued, companyId) <> (Computer.tupled, Computer.unapply)
      }
    
      val computers = TableQuery[Computers]
    }
    
    @Singleton()
    class ComputersDAO @Inject() (protected val dbConfigProvider: DatabaseConfigProvider) extends CompaniesMapping with ComputersMapping
      with HasDatabaseConfigProvider[JdbcProfile] {
      import driver.api._
    
      /** Retrieve a computer from the id. */
      def findById(id: Long): Future[Option[Computer]] =
        db.run(computers.filter(_.id === id).result.headOption)
    
      /** Count all computers. */
      def count(): Future[Int] = {
        // this should be changed to
        // db.run(computers.length.result)
        // when https://github.com/slick/slick/issues/1237 is fixed
        db.run(computers.map(_.id).length.result)
      }
      /** Count computers with a filter. */
      def count(filter: String): Future[Int] = {
        db.run(computers.filter { computer => computer.name.toLowerCase like filter.toLowerCase }.length.result)
      }
    
      /** Return a page of (Computer,Company) */
      def list(page: Int = 0, pageSize: Int = 10, orderBy: Int = 1, filter: String = "%"): Future[Page[(Computer, Company)]] = {
    
        val offset = pageSize * page
        val query =
          (for {
            (computer, company) <- computers joinLeft companies on (_.companyId === _.id)
            if computer.name.toLowerCase like filter.toLowerCase
          } yield (computer, company.map(_.id), company.map(_.name)))
            .drop(offset)
            .take(pageSize)
    
        for {
          totalRows <- count(filter)
          list = query.result.map { rows => rows.collect { case (computer, id, Some(name)) => (computer, Company(id, name)) } }
          result <- db.run(list)
        } yield Page(result, page, offset, totalRows)
      }
    
      /** Insert a new computer. */
      def insert(computer: Computer): Future[Unit] =
        db.run(computers += computer).map(_ => ())
    
      /** Insert new computers. */
      def insert(computers: Seq[Computer]): Future[Unit] =
        db.run(this.computers ++= computers).map(_ => ())
    
      /** Update a computer. */
      def update(id: Long, computer: Computer): Future[Unit] = {
        val computerToUpdate: Computer = computer.copy(Some(id))
        db.run(computers.filter(_.id === id).update(computerToUpdate)).map(_ => ())
      }
    
      /** Delete a computer. */
      def delete(id: Long): Future[Unit] =
        db.run(computers.filter(_.id === id).delete).map(_ => ())
    
    }
    

    添加配置(application.config):

    play.modules.enabled += "bootstrap.ComputersDatabaseModule"
    

    【讨论】:

    • 我们在播放框架项目中是否有针对此问题的问题跟踪器?
    【解决方案2】:

    在 Play 2.4 和更新版本中,slick 插件不再创造进化。为此,我在开发模式中添加了一个页面,该页面始终显示浏览器中所有演变的最新版本。这种方法与模块系统中所有即将发生的变化兼容,因为它不使用任何模块。使用依赖注入,它完全使用您在 application.conf 文件中配置的 Slick 数据库驱动程序

    我在 config/routes 中添加了以下行:

    GET /evolutions.sql                      controllers.SchemaEvolutionsController.evolutions
    

    然后我创建了一个控制器(app/controllers/SchemaEvolutionsController.scala

    package controllers
    
    import com.google.inject.Inject
    import dao.CatDao
    import models.HasSchemaDescription
    import models.HasSchemaDescription.SqlSchemaDescription
    import play.api.Environment
    import play.api.mvc.{Action, Controller}
    import play.api.Mode
    
    class SchemaEvolutionsController @Inject() (environment: Environment, catDao : CatDao) extends Controller {
    
      def allSchemas : Seq[HasSchemaDescription] = List(catDao) // List all schemas here
    
      def descriptionsForAllSchemas : Seq[SqlSchemaDescription] = allSchemas.map(_.schemaDescription)
    
      def evolutions = Action {
        environment.mode match {
          case Mode.Prod => NotFound
          case _ => Ok(views.txt.evolutions(descriptionsForAllSchemas)).withHeaders(CONTENT_TYPE -> "text/plain")
        }
      }
    }
    

    对于这个控制器当然有对应的视图(views/evolutions.scala.txt

    @import models.HasSchemaDescription.SqlSchemaDescription
    @(schemaDescriptions : Seq[SqlSchemaDescription])
    
    # Get the newest version of this evolutions script on the address
    # http://localhost:9000@(controllers.routes.SchemaEvolutionsController.evolutions)
    # when the server runs in development mode
    
    
    # --- !Ups
    
    @for(
        schemaDescription <- schemaDescriptions;
        statement <- schemaDescription.createStatements) {
    @(statement.replaceAll(";",";;"));
    }
    
    # --- !Downs
    
    @for(
        schemaDescription <- schemaDescriptions;
        statement <- schemaDescription.dropStatements) {
    @(statement.replaceAll(";",";;"));
    }
    

    对于 DAO 对象,我添加了一个通用特征来获取架构描述 (app/models/HasSchemaDescription):

    package models
    
    import models.HasSchemaDescription.SqlSchemaDescription
    
    trait HasSchemaDescription {
      def schemaDescription: SqlSchemaDescription
    }
    
    object HasSchemaDescription {
      type SqlSchemaDescription = slick.profile.SqlProfile#SchemaDescription
    }
    

    现在对于每个 DAO 对象,我必须实现 trait 并将 DAO 添加到 SchemaEvolutionsController

    例如用于服务 cat 对象的 DAO:

    class CatDao @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)
      extends HasDatabaseConfigProvider[JdbcProfile] with HasSchemaDescription {
      import driver.api._
    
      private val Cats = TableQuery[CatsTable]
    
      def schemaDescription : SqlSchemaDescription = Cats.schema
    
      def findById(id : Int) : Future[Option[Cat]] =  db.run(Cats.filter(_.id === id).result.headOption)
    
      private class CatsTable(tag: Tag) extends Table[Cat](tag, "CAT") {
    
        def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
        def name = column[String]("NAME")
        def color = column[String]("COLOR")
        def age = column[Option[Int]]("AGE")
    
        def * = (id, name, color, age) <> (Cat.tupled, Cat.unapply _)
      }
    }
    

    通过此示例,您在http://localhost:9000/evolutions.sql 上得到以下结果

    # Get the newest version of this evolutions script on the address
    # http://localhost:9000/evolutions.sql
    # when the server runs in development mode
    
    
    # --- !Ups
    
    
    create table `CAT` (`ID` INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,`NAME` TEXT NOT NULL,`COLOR` TEXT NOT NULL,`AGE` INTEGER);
    
    
    # --- !Downs
    
    
    drop table `CAT`;
    

    【讨论】:

      猜你喜欢
      • 2016-07-24
      • 1970-01-01
      • 2020-08-02
      • 2013-12-13
      • 2014-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多