如何编写数据库无关的播放应用程序,并执行第一次数据库初始化?(How to write databa

2019-07-04 09:22发布

我使用的油滑与游戏框架 2.1和我有一些麻烦。

考虑下面的实体...

package models

import scala.slick.driver.PostgresDriver.simple._

case class Account(id: Option[Long], email: String, password: String)

object Accounts extends Table[Account]("account") {
  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def email = column[String]("email")
  def password = column[String]("password")
  def * = id.? ~ email ~ password <> (Account, Account.unapply _)
}

...我有导入包为特定数据库驱动程序,但我想用H2生产 测试PostgreSQL。 我应该怎么做?

我能够通过重写我的单元测试驱动程序设置来解决此:

package test

import org.specs2.mutable._

import play.api.test._
import play.api.test.Helpers._

import scala.slick.driver.H2Driver.simple._
import Database.threadLocalSession

import models.{Accounts, Account}

class AccountSpec extends Specification {

  "An Account" should {
    "be creatable" in {
      Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver") withSession {
        Accounts.ddl.create                                                                                                                                          
        Accounts.insert(Account(None, "user@gmail.com", "Password"))
        val account = for (account <- Accounts) yield account
        account.first.id.get mustEqual 1
      }
    }
  }
}

我不喜欢这个解决方案,我不知道是否有写DB无关的代码,以便有使用了两种不同的数据库引擎一种优雅的方式 - 一个在测试中,另一个在生产?

我不想用进化,要么,并宁愿让油滑创建数据库表对我来说:

import play.api.Application
import play.api.GlobalSettings
import play.api.Play.current
import play.api.db.DB

import scala.slick.driver.PostgresDriver.simple._
import Database.threadLocalSession

import models.Accounts

object Global extends GlobalSettings {

  override def onStart(app: Application) {
    lazy val database = Database.forDataSource(DB.getDataSource())

    database withSession {
      Accounts.ddl.create
    }
  }
}

我第一次启动该应用程序,一切正常......然后,当然,我第二次开始崩溃,因为表已经在PostgreSQL数据库中存在的应用程序。

这就是说,我的最后两个问题是:

  1. 如何确定数据库表是否不存在?
  2. 我怎样才能让onStart上述DB无关的方法,这样我可以测试我的应用FakeApplication

Answer 1:

你找到关于如何使用饼图案/依赖注入解耦从数据库访问层这里的油滑司机的例子: https://github.com/slick/slick-examples 。

如何去耦油滑驱动程序和测试应用程序与FakeApplication

前几天我写了一个娱乐油滑集成库,其中移动驱动程序相关的播放项目的application.conf: https://github.com/danieldietrich/slick-integration 。

使用该库示例将实现如下的帮助:

1)添加的依赖性项目/ Build.scala

"net.danieldietrich" %% "slick-integration" % "1.0-SNAPSHOT"

添加快照资源库

resolvers += "Daniel's Repository" at "http://danieldietrich.net/repository/snapshots"

或者本地仓库,如果光滑,整合当地出版

resolvers += Resolver.mavenLocal

2)添加斯利克驱动程序的conf / application.conf

slick.default.driver=scala.slick.driver.H2Driver

3)实现应用程序/模型/ Account.scala

在光滑整合的情况下,假设你使用的是自动递增型长的主键。 PK的名字是“身份证”。 表/映射器实现了默认的方法(删除的findAll,findById,INSERT,UPDATE)。 你的实体必须实现“withId”这是由“插入”方法需要。

package models

import scala.slick.integration._

case class Account(id: Option[Long], email: String, password: String)
    extends Entity[Account] {
  // currently needed by Mapper.create to set the auto generated id
  def withId(id: Long): Account = copy(id = Some(id))
}

// use cake pattern to 'inject' the Slick driver
trait AccountComponent extends _Component { self: Profile =>

  import profile.simple._

  object Accounts extends Mapper[Account]("account") {
    // def id is defined in Mapper
    def email = column[String]("email")
    def password = column[String]("password")
    def * = id.? ~ email ~ password <> (Account, Account.unapply _)
  }

}

4)实现应用程序/模型/ DAL.scala

这是用于由控制器来访问数据库的数据访问层(DAL)。 事务是由相应的组件内的表/映射器执行处理。

package models

import scala.slick.integration.PlayProfile
import scala.slick.integration._DAL
import scala.slick.lifted.DDL

import play.api.Play.current

class DAL(dbName: String) extends _DAL with AccountComponent
    /* with FooBarBazComponent */ with PlayProfile {

  // trait Profile implementation
  val profile = loadProfile(dbName)
  def db = dbProvider(dbName)

  // _DAL.ddl implementation
  lazy val ddl: DDL = Accounts.ddl // ++ FooBarBazs.ddl

}

object DAL extends DAL("default")

5)实施的测试/检验/ AccountSpec.scala

package test

import models._
import models.DAL._
import org.specs2.mutable.Specification
import play.api.test.FakeApplication
import play.api.test.Helpers._
import scala.slick.session.Session

class AccountSpec extends Specification {

  def fakeApp[T](block: => T): T =
    running(FakeApplication(additionalConfiguration = inMemoryDatabase() ++
        Map("slick.default.driver" -> "scala.slick.driver.H2Driver",
          "evolutionplugin" -> "disabled"))) {
      try {
        db.withSession { implicit s: Session =>
          try {
            create
            block
          } finally {
            drop
          }
        }
      }
    }

  "An Account" should {
    "be creatable" in fakeApp {
      val account = Accounts.insert(Account(None, "user@gmail.com", "Password"))
      val id = account.id
      id mustNotEqual None 
      Accounts.findById(id.get) mustEqual Some(account)
    }
  }

}

如何确定数据库表是否不存在

我不能给你足够的这个问题的答案...

...但也许这是不是真的s.th你想做的事情。 如果你添加一个属性的表,说Account.active ? 如果你想安全当前存储在您的表中的数据,那么ALTER脚本将做的工作。 目前,这样的ALTER脚本进行手写。 该DAL.ddl.createStatements可以用来检索创建语句。 他们应该被分类到与以前版本更好的可比性。 然后,DIFF(与以前的版本)来手动创建ALTER脚本。 在这里,变阵来改变数据库架构。

下面是关于如何生成(第一)进化的例子:

object EvolutionGenerator extends App {

  import models.DAL

  import play.api.test._
  import play.api.test.Helpers._

    running(FakeApplication(additionalConfiguration = inMemoryDatabase() ++
        Map("slick.default.driver" -> "scala.slick.driver.PostgresDriver",
          "evolutionplugin" -> "disabled"))) {


    val evolution = (
      """|# --- !Ups
         |""" + DAL.ddl.createStatements.mkString("\n", ";\n\n", ";\n") +
      """|
         |# --- !Downs
         |""" + DAL.ddl.dropStatements.mkString("\n", ";\n\n", ";\n")).stripMargin

    println(evolution)

  }

}


Answer 2:

我也试图解决这个问题:切换测试和生产之间数据库的能力。 包裹在一个特质每个表对象的想法是没有吸引力。

我不想在这里讨论的蛋糕模式的优点和缺点,但我发现了另一个解决方案,对于那些有兴趣谁。

基本上,作出这样的对象:

package mypackage
import scala.slick.driver.H2Driver
import scala.slick.driver.ExtendedProfile
import scala.slick.driver.PostgresDriver

object MovableDriver {
  val simple = profile.simple
  lazy val profile: ExtendedProfile = {
    sys.env.get("database") match {
      case Some("postgres") => PostgresDriver
      case _ => H2Driver
    }
  }
}

很显然,你可以做你喜欢这里的任何决策逻辑。 它没有被基于系统性能。

现在,而不是:

import scala.slick.driver.H2Driver.simple._

你可以说

import mypackage.MovableDriver.simple._

更新:一个漂亮的3.0版本,特伦特 - 阿伦斯的礼貌:

package mypackage

import com.typesafe.config.ConfigFactory

import scala.slick.driver.{H2Driver, JdbcDriver, MySQLDriver}

object AgnosticDriver {
  val simple = profile.simple
  lazy val profile: JdbcDriver = {
    sys.env.get("DB_ENVIRONMENT") match {
      case Some(e) => ConfigFactory.load().getString(s"$e.slickDriver") match {
        case "scala.slick.driver.H2Driver" => H2Driver
        case "scala.slick.driver.MySQLDriver" => MySQLDriver
      }
      case _ => H2Driver
    }
  }
}


Answer 3:

该播放光滑不完全一样,什么是在其他的答案提出的,这似乎是播放/类型安全的保护伞下。

你只可以导入import play.api.db.slick.Config.driver.simple._ ,它会根据需要选择相应的驱动程序conf/application.conf

它也提供了一些类似的连接池,DDL生成更多的事情......



Answer 4:

如果像我一样,你不使用游戏! 对于该项目,一个解决方案是由Nishruu提供这里



文章来源: How to write database-agnostic Play application and perform first-time database initialization?