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 _)
}
...特定のデータベースドライバーのパッケージをインポートする必要がありますが、H2testingおよびPostgreSQLforproduction。どうすればいいですか?
私はユニットテストでドライバー設定をオーバーライドすることでこれを回避することができました:
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, "[email protected]", "Password"))
val account = for (account <- Accounts) yield account
account.first.id.get mustEqual 1
}
}
}
}
私はこのソリューションが気に入らず、DBに依存しないコードを書くための洗練された方法があるので、2つの異なるデータベースエンジンが使用されています。1つはテスト用で、もう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
}
}
}
最初にアプリケーションを起動すると、すべてが正常に動作します...もちろん、アプリケーションを2回目に起動すると、テーブルがすでにPostgreSQLデータベースに存在するため、アプリケーションがクラッシュします。
とはいえ、私の最後の2つの質問は次のとおりです。
onStart
メソッドをDB-agnosticの上に作成して、FakeApplication
でアプリケーションをテストできるようにするにはどうすればよいですか?ケーキパターン/依存関係注入を使用して、データベースアクセスレイヤーから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"
または、slick-integrationがローカルで公開されている場合は、ローカルリポジトリ
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)です。トランザクションは、対応するコンポーネント内のテーブル/マッパー実装によって処理されます。
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, "[email protected]", "Password"))
val id = account.id
id mustNotEqual None
Accounts.findById(id.get) mustEqual Some(account)
}
}
}
この質問に十分な答えを出すことはできません...
...しかし、おそらくこれは本当にあなたがやりたいことではありません。テーブルに属性を追加する場合はどうでしょう、たとえばAccount.active
?現在テーブル内に格納されているデータを保護したい場合は、alterスクリプトが役立ちます。現在、そのような変更スクリプトは手動で作成する必要があります。 DAL.ddl.createStatements
を使用して、作成ステートメントを取得できます。以前のバージョンと比較できるように並べ替える必要があります。次に、差分(以前のバージョン)を使用して、変更スクリプトを手動で作成します。ここでは、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)
}
}
私はこの問題にも対処しようとしていました。データベースをテストと本番の間で切り替える機能です。各テーブルオブジェクトを特性にラップするという考えは魅力的ではありませんでした。
ここでは、ケーキパターンの長所と短所については説明しませんが、興味のある方のための別の解決策を見つけました。
基本的には、次のようなオブジェクトを作成します。
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
}
}
}
play-slick は、他の回答で提案されているものとまったく同じであり、Play/Typesafeの傘下にあるようです。
import play.api.db.slick.Config.driver.simple._
をインポートするだけで、conf/application.conf
に従って適切なドライバが選択されます。
また、接続プーリング、DDL生成などの機能も提供します。