ScalaQueryの使い方&サンプル集
Scala Advent Calendar jp 2011 - PARTAKEの2日目の記事です。
Scala から RDB を使う必要があり、 ScalaQuery をいろいろ調べてみました。
DBオブジェクトの取得
Database.forURL とかを使います。
scala> import org.scalaquery.session.Database import org.scalaquery.session.Database scala> val db = Database.forURL("jdbc:postgresql:scala_query_example", driver="org.postgresql.Driver", user="user", password="password") db: org.scalaquery.session.Database = org.scalaquery.session.Database$$anon$2@5b34329b
DBへの接続
withSession、withTransaction を使います。
おなじみLoanPatternです。
withSession、withTransactionのブロックを抜けると自動的に接続が切れます。
逆に言うと、このブロックの中でしかクエリは発行できません。
db withSession { /* この中でいろいろやる */} db withTransaction { /* この中でいろいろやる */}
テストデータ
ビートルズのみなさんです。
CREATE TABLE beatles ( id INTEGER PRIMARY KEY , first_name TEXT , last_name TEXT ); INSERT INTO beatles (id, first_name, last_name) VALUES (1, 'John', 'Lennon'); INSERT INTO beatles (id, first_name, last_name) VALUES (2, 'Paul', 'McCartney'); INSERT INTO beatles (id, first_name, last_name) VALUES (3, 'George', 'Harrison'); INSERT INTO beatles (id, first_name, last_name) VALUES (4, 'Peter', 'Best');
> select * from beatle; id | first_name | last_name ----+------------+----------- 1 | John | Lennon 2 | Paul | McCartney 3 | George | Harrison 4 | Peter | Best
SQLをそのまま実行
org.scalaquery.simple.StaticQueryを使えばSQLをそのまま使えます。
ScalaQuery で SQL を実行する - tototoshiの日記
これもなかなか便利ですが、やっぱORMとして使いたいですよね。
ってことで
テーブルの定義
org.scalaquery.ql.basic 以下のBasicTableを使います。
ExtendedTable ていうのもあるけど各DB固有の機能のものかな?大抵はBasicのほうで事足りるでしょう。
テーブルは
Table[(カラムの型)]("テーブル名")
カラムは
column[カラムの型]("カラム名")
という書き方をします。
カラムにはオプション(NotNull, PrimaryKeyなど)も渡せます
* というメソッドは必須です。
scala> import org.scalaquery.ql._ import org.scalaquery.ql._ scala> import basic.{ BasicTable => Table } import basic.{BasicTable=>Table} scala> object Beatles extends Table[(Int, String, String)]("beatles") { | def id = column[Int]("id", O PrimaryKey) | def first_name = column[String]("first_name") | def last_name = column[String]("last_name") | def * = id~first_name~last_name | } defined module Beatles
クエリの組み立て
Implicit Conversion の import を忘れずに!!
一応ドキュメントにも載っているのだけどわかりにくい。
scala> import org.scalaquery.ql.basic.BasicDriver.Implicit._ /* これを忘れずに!! */ import org.scalaquery.ql.basic.BasicDriver.Implicit._ scala> val q = for (b <- Beatles) yield { b.* } q: org.scalaquery.ql.Query[(Int, java.lang.String, java.lang.String)] = Query scala> val q2 = for (b <- Beatles) yield { b.first_name ~ b.last_name } q2: org.scalaquery.ql.Query[(java.lang.String, java.lang.String)] = Query
scala のコレクションのように、DBのテーブルを扱うことができます。
クエリの実行
list() などはimplicitにSessionを要求するのでこんな書き方をします。
scala> import org.scalaquery.session.Session import org.scalaquery.session.Session scala> db withSession { implicit s: Session => q.list() } res4: List[(Int, java.lang.String, java.lang.String)] = List((1,John,Lennon), (2,Paul,McCartney), (3,George,Harrison), (4,Ringo,Star)) scala> db withSession { implicit s: Session => q2.list() } res5: List[(java.lang.String, java.lang.String)] = List((John,Lennon), (Paul,McCartney), (George,Harrison), (Ringo,Star))
org.scalaquery.session.Database.threadLocalSession(implicit定義されている) を importする手もあります。
scala> import org.scalaquery.session.Database.threadLocalSession import org.scalaquery.session.Database.threadLocalSession scala> db withSession { q.list() } res7: List[(Int, java.lang.String, java.lang.String)] = List((1,John,Lennon), (2,Paul,McCartney), (3,George,Harrison), (4,Peter,Best))
サンプル集
テーブルのCREATE, DROP & CRUD はこんな感じ。
CREATE TABLE
ddl.create を使います。
/* CREATE TABLE beatles ( id INTEGER PRIMARY KEY , first_name TEXT , last_name TEXT ); */ db withSession { implicit s: Session => Beatles.ddl.create }
INSERT
insert, または insertAll を使います。
db withSession { implicit s: Session => Beatles.insert((1, "John", "Lennon")) Beatles.insertAll((2, "Paul", "McCartney"), (3, "George", "Harrison"), (4, "Peter", "Best")) }
SELECT
// SELECT first_name, lastname from beatles where id = 1 val selectWhereIdEqual1 = for (b <- Beatles if b.id === 1) yield b.first_name ~ b.last_name // SELECT first_name, lastname from beatles where id != 1 val selectWhereIdNotEqual1 = for (b <- Beatles if b.id =!= 1) yield b.first_name ~ b.last_name // SELECT * from beatles val selectAll = for (b <- Beatles) yield b.* db withSession { implicit s: Session => val res1 = selectWhereIdEqual1.list() println(res1) // List((John,Lennon)) val res2 = selectWhereIdEqual1.first() println(res2) // (John,Lennon) val res3 = selectWhereIdEqual1.firstOption() println(res3) // Some((John,Lennon)) val res4 = selectWhereIdNotEqual1.list() println(res4) // List((Paul,McCartney), (George,Harrison), (Peter,Best)) val res5 = selectAll.to[Array]() println(res5.mkString("Array(", ", ", ")")) // Array((1,John,Lennon), (2,Paul,McCartney), (3,George,Harrison), (4,Peter,Best)) }
== の代わりに ===
!= の代わりに =!= っていうのを使います。 *1
firstでひとつだけ取ってこれます。
firstOptionも最初のひとつを取ってきますが、なかった場合Noneになります。
ListのheadOptionみたいなもんですね。
first で値がなかった場合、例外飛んで死亡するので気をつけましょう。
UPDATE
ピート・ベストをリンゴに替えましょう
// UPDATE Beatles SET first_name = Ringo, last_name = Starr WHERE first_name == Peter db withSession { implicit s: Session => val q = for (b <- Beatles if b.first_name === "Peter") yield b.first_name ~ b.last_name q.update("Ringo", "Star") val res = selectAll.list() println(res) // List((1,John,Lennon), (2,Paul,McCartney), (3,George,Harrison), (4,Ringo,Star)) }
これはちょっときもいかも。
DELETE
ジョンが死んでしまいました。テーブルから消しましょう。
// DELETE FROM Beatles WHERE id = 1 db withSession { implicit s: Session => val before = selectAll.list() println(before) // List((1,John,Lennon), (2,Paul,McCartney), (3,George,Harrison), (4,Ringo,Starr)) Beatles.where(_.id === 1).delete val after = selectAll.list() println(after) // List((2,Paul,McCartney), (3,George,Harrison), (4,Ringo,Starr)) }
where ってのもあるんですよっと。
DROP TABLE
// drop table beatles // drop table songs db withSession { implicit s: Session => Beatles.ddl.drop }
DROP は CREATE 同様、 ddl を使います。
ここから随時追記ゾーン
Connection Poolを使う
COUNT
2012/02/01
import org.scalaquery.ql.ColumnOps._ val countQuery = Beatles map CountAll db withSession { implicit s: Session => val count = countQuery.first }
OFFSET, LIMIT
take, dropでやります。わかりやすい。
extended driver が必要です。
import org.scalaquery.ql.extended.PostgresDriver.Implicit._ db withSession { implicit s: Session => val result = q.drop(offset).take(limit).list() }
もっと知りたい人は
ここで説明してないこと↓
- ORDER は?
- GROUP BY は ?
- JOIN は?
公式のドキュメントを見ましょう。 http://scalaquery.org/doc/
ドキュメント、いまいち少ないですが、 ScalaQuery_Commerzbank_2011.pdf というスライドはやたら気合いが入っててオススメ。
そのほか、 github に scala-query-example というプロジェクトがあります。
https://github.com/szeiger/scalaquery-examples
それから、scalaqueryのテストコードも参考にできます。
これだけ情報があればとりあえず大丈夫ではないでしょうか!
以上です!
*1:トンボみたい