gpt4 book ai didi

scala - 如何编写与数据库无关的 Play 应用程序并执行首次数据库初始化?

转载 作者:行者123 更新时间:2023-12-03 05:39:36 26 4
gpt4 key购买 nike

我正在使用SlickPlay 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
}
}
}
}

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

我也不想使用进化,更喜欢让 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 数据库中。

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

  1. 如何确定数据库表是否已存在?
  2. 如何使 onStart 方法与数据库无关,以便我可以使用 FakeApplication 测试我的应用程序?

最佳答案

您可以在此处找到有关如何使用蛋糕模式/依赖项注入(inject)将 Slick 驱动程序与数据库访问层解耦的示例:https://github.com/slick/slick-examples .

如何使用 FakeApplication 解耦 Slick 驱动程序和测试应用程序

前几天我为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"

或者本地存储库,如果 slick-integration 是在本地发布的

resolvers += Resolver.mavenLocal

2) 将 Slick 驱动程序添加到 conf/application.conf

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

3) 实现 app/models/Account.scala

在 slick-integration 的情况下,假设您使用自动递增的 Long 类型的主键。 pk 名称是“id”。 Table/Mapper 实现具有默认方法(delete、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) 实现 app/models/DAL.scala

这是数据访问层(DAL), Controller 使用它来访问数据库。事务由相应组件内的表/映射器实现处理。

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)会怎样?如果您想保护当前存储在表中的数据,那么更改脚本就可以完成这项工作。目前,这样的更改脚本必须手动编写。 DAL.ddl.createStatements 可用于检索创建语句。应该对它们进行排序,以便更好地与以前的版本进行比较。然后使用 diff(与以前的版本)来手动创建更改脚本。在这里,演化用于改变数据库模式。

这是一个关于如何生成(第一个)进化的示例:

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)

}

}

关于scala - 如何编写与数据库无关的 Play 应用程序并执行首次数据库初始化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13661339/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com