Как написать приложение database-agnostic Play и выполнить первую инициализацию базы данных?
я использую пятно С 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-agnostic, поэтому используются два разных ядра баз данных - один в тестировании, а другой в производстве?
Я также не хочу использовать 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
метод выше DB-agnostic, так что я могу проверить мое приложение сFakeApplication
?
4 ответа:
вы найдете пример того, как использовать Cake pattern / dependency injection, чтобы отделить драйвер Slick от уровня доступа к базе данных здесь:https://github.com/slick/slick-examples.
как отделить драйвер Slick и тестовое приложение с помощью FakeApplication
несколько дней назад я написал гладкую библиотеку интеграции для play, которая перемещает зависимость драйвера в приложение.конф проекта играть : https://github.com/danieldietrich/slick-integration.
С помощью этой библиотеки ваш пример будет осуществляться следующим образом:
1) добавьте зависимость к проекту / сборке.скала
"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) реализовать приложение / модели / аккаунт.скала
в случае Слик-интеграции предполагается, что вы используете первичные ключи типа Long, которые автоматически увеличиваются. ПК имя является 'идентификатор'. Таблица/реализации маппер имеет стандартные методы (удаление, метод findAll, findbyid осуществляет, вставка, обновление). Ваши сущности должны реализовать 'withId', который необходим методом' insert'.
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.скала
это уровень доступа к данным (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) реализовать тест/тест/AccountSpec.скала
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
может использоваться для получения инструкций create. Они должны быть отсортированы, чтобы быть лучше сопоставимы с предыдущими версиями. Затем 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) } }
Я также пытался решить эту проблему: возможность переключения баз данных между тестом и производством. Идея обернуть каждый объект таблицы в черту была непривлекательной.
Я не пытаюсь обсуждать плюсы и минусы шаблон торт здесь, но я нашел другое решение, для тех, кто заинтересован.
в принципе, сделать объект вроде этого:
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, любезно предоставленная 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 } } }
The play-slick делает точно так же, как то, что предлагается в других ответах, и, похоже, находится под зонтиком Play/Typesafe.
вы просто можете импортировать
import play.api.db.slick.Config.driver.simple._
и он выберет соответствующий драйвер в соответствии сconf/application.conf
.Он также предлагает еще несколько вещей, таких как пул соединений, генерация DDL...
Если, как и я, вы не используете Play! для проекта решение предоставляется Nishruu здесь