Slickのドライバーを定義ファイルで指定する方法
Playで開発をしていて、テストや開発の序盤はとりあえずH2を使って、本番ではMySQLを使う場合、Slickのドライバーをimport scala.slick.driver.H2Driverのように直接指定してしまうと、後々すべてのモデルのソースを変更していかなければいけなくて、なんかいい方法無いかなと探してたらこんな記事を見つけたので、これを参考に自分なりに対応してみました。
※ サンプルコードをgithubにあげています。
Source code:yyhayashi303/play21-slick · GitHub
今回の環境は以下のとおり。
play-2.1.0 slick-1.0.0 scala-2.10.0
まずは、定義ファイルからドライバーのインスタンスを生成するトレイトを下記のように実装します。
各ドライバーはExtendedProfileを継承しているので、返されるインスタンスの型はExtendedProfileを指定します。
import play.api.Application import slick.driver.ExtendedProfile trait Profile { val SLICK_DRIVER = "slick.db.driver" val DEFAULT_SLICK_DRIVER = "scala.slick.driver.H2Driver" def getProfile(implicit app: Application): ExtendedProfile = { val driverClass = app.configuration.getString(SLICK_DRIVER).getOrElse(DEFAULT_SLICK_DRIVER) singleton[ExtendedProfile](driverClass) } private def singleton[T](name : String)(implicit man: Manifest[T]): T = Class.forName(name + "$").getField("MODULE$").get(man.runtimeClass).asInstanceOf[T] }
次に、Tableクラスを継承したBaseTableクラスを定義したいのですが、そもそもTableクラスを使う時点でimport scala.slick.driver.H2.simple._としないといけないので、ひと工夫する必要があります。
先ほど作ったProfileトレイトを継承したBaseModelクラスを作成し、そのクラス内に入れ子にしたクラスとしてBaseTableクラスを定義します。
import play.api.Play.current import play.api.db.DB import slick.driver.ExtendedProfile object BaseModel extends Profile { lazy val profile: ExtendedProfile = getProfile import profile.simple._ def withSession[T](f: (Session => T)): T = Database.forDataSource(DB.getDataSource()).withSession(f) abstract class BaseTable[T](name: String) extends Table[T](name) }
BaseModelの中でprofileを取得し、import profile.simple._とすることで、同じスコープ内にあるBaseTableが継承するTableクラスを解決することができます。
最後にこのBaseTableを継承してモデルクラスを作成します。
import common.BaseModel case class User(id: Int, firstName: String, lastName: String, email: String) object Users extends BaseModel.BaseTable[User]("USER") { import BaseModel.profile.simple._ def id = column[Int]("id", O PrimaryKey, O AutoInc) def firstName = column[String]("first_name", O DBType "varchar(20)") def lastName = column[String]("last_name", O DBType "varchar(20)") def email = column[String]("email", O DBType "varchar(32)") def * = id ~ firstName ~ lastName ~ email <> (User.apply _, User.unapply _) def ins = firstName ~ lastName ~ email returning id }
あとは、application.confにドライバーの定義を記述します。
# デフォルトはH2 slick.db.driver=scala.slick.driver.H2Driver
各環境用の定義ファイル(例えばdevel.conf)にはドライバーの定義を上書くよう記述します。
include "application.conf" slick.db.driver=scala.slick.driver.MySQLDriver
そして、アプリケーションを起動する際に下記のように定義ファイルを指定することでドライバーを切り替えられるようになります。
play -Dconfig.file=conf/devel.conf ~run