Develop with pleasure!

福岡でCloudとかBlockchainとか。

Play2 scala with Squeryl

Play2 Scalaの構成で標準のモデル層のサポートはAnormになる。AnormはORMではなく、シンプルにSQLを書くデータアクセスレイヤーのコンポーネント

SQL書くのはちょっとなーという印象だけど、Scala的には高レベルのORMは非生産的と捉えられ、SQLは最適なDSLとして捉えられてるみたい。ただScalaビギナーにとっては、その辺の実感がまだ無いわけで確かにSQLは言語的に素晴らしいDSLだとも思うけれど、やっぱりORM使いたいと思うわけで。

Squerylを導入してみた。SquerylはScalaベースのORM。

セットアップ

Build.scalaにライブラリリの依存性を定義。

object ApplicationBuild extends Build {
...
  val appDependencies = Seq(
    // Add your project dependencies here,
    "org.squeryl" %% "squeryl" % "0.9.5-5"
  )
...
}

app/Global.scalaというファイルを作成し初期化処理を記載する。↓の例ではH2DBしか定義してないけど、必要であれば使用するDBのアダプタをcase文で定義する。

import org.squeryl.adapters.H2Adapter
import org.squeryl.internals.DatabaseAdapter
import org.squeryl.{Session, SessionFactory}
import play.api.db.DB
import play.api.GlobalSettings
import play.api.Application
/**
 * アプリケーション全般の設定を行うオブジェクト
 */
object Global extends GlobalSettings{

  /**
   * アプリケーション起動時のイベントをフック
   * @param app
   */
  override def onStart(app: Application) {
    // Squerylの初期化
    SessionFactory.concreteFactory = app.configuration.getString("db.default.driver") match {
      case Some("org.h2.Driver") => Some(() => getSession(new H2Adapter, app))
      case _ => sys.error("Database driver must be either org.h2.Driver") 
    }
  }

  def getSession(adapter:DatabaseAdapter, app:Application) = Session.create(DB.getConnection()(app), adapter)

}

モデルの作成

続いてモデルを作成する。app/models/Person.scalaというモデルを作成。

package models

import org.squeryl.KeyedEntity
import org.squeryl.annotations.Column

case class Person(
                  @Column("first_name")
                  fiestName: String,
                  @Column("first_name")
                  lastName: String,
                  sex: String
             ) extends KeyedEntity[Long]{

  val id: Long = 0
}

DBのカラム名とモデルのフィールド名が合致しない場合は@Columnアノテーションカラム名を定義可能。
KeyedEntityを継承してるのは、idというauto incrementなプライマリキー使用するため。ただこのKeyedEntityは廃止されKeyedEntityDefに置き換わるらしい。Squeryl の KeyedEntity がオワコンになっていた - tototoshiの日記

モデルの定義をしたので続いてスキーマ定義。スキーマオブジェクトを作成する必要があるので、app/models/SampleSchema.scalaを作成する。

package models

import org.squeryl.Schema

object SampleSchema extends Schema{

  val persion = table[Person]("person")

}

("person")は省略可能で、モデル名とテーブル名が異なる際に使用する。

DDLの作成

テーブルの作成や削除は、↑のSampleSchema.create or dropすればDBに接続してテーブルの作成・削除が可能ではあるが今回はPlay Framework使ってるのでPlayのエボリューションスクリプトでDDLを作成する。エボリューションスクリプトはconf/evolutions/default/フォルダ直下に1.sql、2.sqlといった命名でつくっていく。エボリューションスクリプトの仕様についてはDocumentation: Evolutions — Playframework参照。

# Tasks schema

# --- !Ups

CREATE TABLE person (
    id serial primary key,
    first_name varchar(255) NOT NULL,
    last_name varchar(255) NOT NULL,
    sex varchar(255) NOT NULL
);

# --- !Downs

DROP TABLE person;

スキーマ定義をバージョン管理できるとう意味ではエボリューションスクリプトを書くべきだと思うけど、SQLDDLを書いてくのがちょっとなー。DSLでスキーマ定義書けたりすると可読性が上がって便利な気がする。まぁSQLなので単純明快で分かりやすいことは分かりやすいんだけど。

あとはplay runすればアプリケーションが立ち上がり、まだDBに反映していないエボリューションスクリプトがあれば適用するか画面で聞いてくるのでクリックするだけでスキーマ定義がDBに反映される。何気にこれは便利な機能。