PlayでDBを扱うための設定周りを書く。application.confとかevolutionsあたり。
Play2.0.4。DBはMySQL5.1を使用。
Play2.0ではAnormという割と生な感じのライブラリを使って記述する。
AnormはRailsのActiveRecordとかとは違ってORMではない。Anormを使うとConnectionとかResultSetとかの生々しい子たちとはお別れできるけど、具体的なDB操作はSQLを直接書いて行う。
具体的にはこんな感じ。
DB.withConnection { implicit connection =>
SQL("SELECT clm1, clm2 hoge WHERE id = {id}").on("id" -> 1).apply() foreach {
case Row(clm1: String, clm2: String) => println(clm1, clm2)
}
}
SQLを直で書く理由は、「RDB操作するDSLってわざわざ新開発しなくても、SQLが一番良くね? ベンダーごとに差があることを加味してもさ」というようなポリシーから来ているらしい。
実際問題としてORMでは間に合わないことは多いので、こういうはっきりした方向性は嫌いじゃないわ。
Squeryl等のORMライブラリを併用することは可能。そのうちSlickとかを絡める話も出てくるかもしれない。SlickはScala2.9系には対応してないので当面は無理そうだけど。
Playをダウンロードしたフォルダの中のsamples/scala/computer-databaseにその手のサンプルが書かれている。
そこの動きを見ながらapp/models/Models.scalaとかconf/evolutionsの中とかを見るとなんとなく仕組みが分かった気持ちになる。
Evolutions(SQLを書いておく場所)に関する話はこの辺が参考になる。
とりあえず今回のサンプル用のプロジェクトとDBを作る。プロジェクト名とDB名は共にplay_example(仮)としておく。
プロジェクト作成。
$ play new play_example $ cd play_example
データベース作成。
mysql> create database play_example;
準備完了。
MySQLでDBに接続することをPlayに伝える。
その辺りの設定はconf/application.confに書く。application.confには下記のような記述がある。
# db.default.driver=org.h2.Driver # db.default.url="jdbc:h2:mem:play" # db.default.user=sa # db.default.password=
これをMySQLに繋ぐっぽい記述に編集する。データベース名は先ほど作成したplay_exampleを利用。
db.default.driver=com.mysql.jdbc.Driver db.default.url="jdbc:mysql://localhost/play_example?useUnicode=yes&characterEncoding=UTF-8" db.default.user=user db.default.password=password
あと、mysql-connector-javaを依存jarに入れておく。project/Build.scalaを編集して、appDependenciesに太字の部分を追加。
val appDependencies = Seq( // Add your project dependencies here, "mysql" % "mysql-connector-java" % "5.1.21" )
これで設定完了。
Playではconf/evolutions/${データベース名}の下に1.sql、2.sql、3.sqlのように連番でファイルを置くと、そのファイルが番号順に実行されるらしい。
データベース名はMySQLでのDB名ではなく、db.default.driverみたいに書いたとこのdefaultを指すようだ。
というわけで、conf/evolutions/default/1.sqlにCREATE文を書いてみる。
$ mkdir -p conf/evolutions/default $ vi conf/evolutions/default/1.sql
下記のような感じでSQLを書く。
# --- !Ups
CREATE TABLE user (
id int(10) NOT NULL AUTO_INCREMENT,
name varchar(64) NOT NULL,
email varchar(255) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO user (name, email) VALUES ("おれおれ", "oreore@example.com");
# --- !Downs
DROP TABLE User
# --- !UpsにCREATEを、# --- !DownsにDROPを書いている。
DownsのところにDROP文を書いておくと、1.sqlを更新してサーバに繋いだ時に、DROPを実行してからCREATEを実行してくれる。
INSERT文も実行できるので、試しに書いておいた。
これならPlayを知らない人が見てもスキーマをすぐ理解できる。置かれている場所がconf/evolutionsという名前なのは、少しイメージしづらいかもしれないけど。
evolutionにSQLを用意したら、play runでサーバを立ち上げる。
$ play run
立ち上がったらhttp://localhost:9000/にリクエストする。するとこんなページが出てくる。
上のページを訳すと「defaultデータベースはevolutionを必要としている。下記のSQLを実行するぞ」みたいな。
右上のApply this script now!をクリックすると、画面に表示されたSQL(1.sqlのUpsの部分)が実行される。
1.sqlの内容を編集してもう一度ページを開くと、今度は少し内容が変わって「DROPしてからもう1回作り直すけど良い?」みたいな表示がされる。
Modelと言っても共通のModelクラスを継承してみたいなことはしない。普通にSQLを呼び出すだけのクラスを作ればいいらしい。
試しにapp/models/Users.scalaというファイルを作って、userに対してSELECT COUNT(*)する処理を書いてみる。
package models
import play.api.db._
import play.api.Play.current
import anorm._
import anorm.SqlParser._
object Users {
def countAll: Long = {
DB.withConnection { implicit connection =>
SQL("SELECT COUNT(*) FROM user").as(scalar[Long].single)
}
}
}
あとはapp/controllers/Application.scalaから作ったメソッドを呼んでみる。view書くの面倒だからprintlnで済ます。
package controllers
import play.api._
import play.api.mvc._
object Application extends Controller {
def index = Action {
println( models.Users.countAll )
Ok(views.html.index("Your new application is ready."))
}
}
これでサーバを立ち上げたコンソール上に「1」と表示される。
Anormの詳しい使い方とかはまた別の機会に。