我使用Slick和Play Framework 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无关的代码,因此使用了两种不同的数据库引擎 - 一种在测试中而另一种在生产中?
我也不想使用evolution,并且更喜欢让Slick为我创建数据库表:
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数据库中已经存在表。
那就是说,我的最后两个问题是:
onStart
方法与数据库无关,以便我可以使用FakeApplication
测试我的应用程序?答案 0 :(得分:39)
您可以在此处找到有关如何使用蛋糕模式/依赖项注入将Slick驱动程序与数据库访问层分离的示例:https://github.com/slick/slick-examples。
几天前,我为Play编写了一个Slick集成库,它将驱动程序依赖项移动到Play项目的application.conf:https://github.com/danieldietrich/slick-integration。
在这个库的帮助下,您的示例将按如下方式实现:
1)将依赖项添加到project / Build.scala
"net.danieldietrich" %% "slick-integration" % "1.0-SNAPSHOT"
添加快照存储库
resolvers += "Daniel's Repository" at "http://danieldietrich.net/repository/snapshots"
或本地存储库,如果在本地发布光滑集成
resolvers += Resolver.mavenLocal
2)将Slick驱动程序添加到conf / application.conf
slick.default.driver=scala.slick.driver.H2Driver
3)实施app / models / Account.scala
在光滑集成的情况下,假设您使用自动递增的Long类型的主键。 pk名称是'id'。 Table / Mapper实现具有默认方法(delete,findAll,findById,insert,update)。您的实体必须实现'insert'方法所需的'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)实施app / models / DAL.scala
这是数据访问层(DAL),控制器使用它来访问数据库。事务由相应组件中的Table / Mapper实现处理。
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)实施test / test / 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)
}
}
}
我无法对这个问题给你足够的答案......
...但也许这不是你想做的事情。如果您向表格添加属性,例如Account.active
,该怎么办?如果要保护当前存储在表中的数据,那么alter脚就可以完成这项工作。目前,这种改变脚本必须手工编写。 DAL.ddl.createStatements
可用于检索create语句。它们应该被分类以便与以前的版本更好地比较。然后使用diff(使用以前的版本)手动创建alter脚本。这里,evolutions用于改变db模式。
以下是如何生成(第一个)进化的示例:
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)
}
}
答案 1 :(得分:28)
我还试图解决这个问题:在测试和生产之间切换数据库的能力。将每个表对象包装在特征中的想法没有吸引力。
我不想在这里讨论蛋糕模式的优缺点,但我找到了另一种解决方案,对于那些感兴趣的人。
基本上,制作一个这样的对象:
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._
更新:Slick 3.0版本,由trent-ahrens提供:
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
}
}
}
答案 2 :(得分:2)
play-slick与其他答案中提出的内容完全相同,并且它似乎属于Play / Typesafe的保护范围。
您可以导入import play.api.db.slick.Config.driver.simple._
它会根据conf/application.conf
选择合适的驱动程序。
它还提供了更多的东西,如连接池,DDL生成......
答案 3 :(得分:-1)
如果像我一样,你没有使用Play!对于该项目,Nishruu提供了一个解决方案here