1
Slick入門
Takako Shimamoto
BizReach, Inc.
2
アジェンダ
• Scalaの代表的なORM
• Slickの基本的なお話
– スキーマ定義
– クエリ
• SQLを直接書く方法
– Plain SQL
– その他の選択肢
• Play2 + Slickで使うには
– 設定
– コネクショ...
3
Scalaで使えるORM
• Slick(旧ScalaQuery)
– タイプセーフなDSLや、SQLも記述可能
– 極力Scalaのコレクションと同じように扱えるよう設計され
ている
• Squeryl
– SQLは使用せず、DSLでクエ...
4
Scalaで使えるORM
• Anorm
– SQLを直接書くスタイル
– 手動でORマッピングを行う
• Scala ActiveRecord
– Squerylをベースに、モデル定義やよく使う機能をRailsの
ActiveRecord...
5
他にも、Activate、ScalikeJDBC、SORM など・・・
本日はTypesafe社お墨付きの
Slickについてお話します
6
Slickのきほん
7
タイプセーフなクエリ
• Slickは以下のように、クエリをタイプセーフに記述で
きることが1つの特徴
• このクエリを書くためには、スキーマ定義が必要
val id = 1L
val user: Option[UsersRow] =
Us...
8
スキーマ定義
• でも、手動で書くのは面倒ですよね
// マッピングするケースクラス
case class UserRow(id: Long, name: String)
// テーブルのスキーマ定義
class User(tag: Tag...
9
実はジェネレータがあるんですよ
10
コードジェネレータ
• sbtのタスクとして実行
• 生成するファイル
– Tables.scala
• 生成されるもの
– テーブル定義
– マッピングするケースクラス
– TableQuery
• クエリで使用
– 全テーブルのDDL...
11
コードジェネレータ
• ジェネレータはカスタマイズ可能
– SourceCodeGeneratorを拡張する形
– ドキュメントがないので拡張ポイントがわかりにくい
– slick.model.codegenパッケージ配下を見るとよい
•...
12
これでクエリが書けます
13
基本的なCRUD
// 全件取得
val users: Seq[UserRow] = User.list
// 登録
val res: Int = User insert UserRow(1, "なまえ")
// 更新
val res: ...
14
基本的なCRUD
// 全件取得
val users: Seq[UserRow] = User.list
// 登録
val res: Int = User insert UserRow(1, "なまえ")
// 更新
val res: ...
15
バインド変数になるbind
• bindなし
• bindあり
val name = "ta'kako"
User.filter(_.name is name).firstOption
・・・ from USER x1 where x1....
16
ケースクラスにマッピング
• <>を使えば、mapで絞り込んだ項目を別のケースク
ラスにマッピングできる
// このケースクラスにマッピング
case class Test(id: Long, name: String)
val res:...
17
他にもいろいろ
• ソート、グルーピング、ページング など
• 内部結合、外部結合も可能
• slick-referenceをGitHubに公開中
https://github.com/bizreach/slick-reference
18
タイプセーフなクエリで書けない
ときは?
19
Plain SQL
• SQLを文字列リテラルで書く
• 局所的に使うには便利
• ただ、複雑なSQLの代替として使うには厳しい
– リファクタリングがやりにくい
– 動的なSQLを組み立てるためには文字列処理が必要
sql"""SELE...
20
その他の選択肢
• mirage-scala
– Seasar2の2WaySQLの機能を単独のライブラリとして利
用できるようにしたもの
– Slickと組み合わせて使用することも可能
val books: List[Book] = sq...
21
Play2でSlickを使うには
22
play-slick
• Play2でSlickを使う上で、面倒なことをいろいろやっ
てくれるプラグイン
– Play2の設定から自動的にSlickの
Database.forDataSourceを呼び出してくれる
– Slickのセッシ...
23
何が変わるの?
• コントローラでPlay2のActionの代わりに、play-slick
のDBActionを使う
• トランザクションを開始する場合は
DBAction.transactionを使う
def list = DBActi...
24
コネクションプール
• Slickはコネクションプールの実装を持っていない
• Play2と組み合わせて使うならBoneCP
• ただし、有名なリーク問題が未だにある
– https://bugs.launchpad.net/bonecp...
25
プロジェクトで使う上で注意
することは?
26
留意事項
• アグレッシブな機能追加/削除
– Scala全体の傾向(Slickも例外ではない)
– 特にメジャーアップにはマイグレーションが必要
– 常にキャッチアップして対応していく必要がある
• マニュアルがあまり豊富でない
– 本...
27
留意事項
• 結合は発行されるSQLに注意
– 単一テーブルなら、①fiterで絞り込む、②mapで取得項目
を決める、③listやfirstOptionで実行、という流れ
– 結合は、メソッドチェーンとfor式で発行するSQLが異なる
...
28
留意事項
• Play2のEvolutionsは実践には厳しい
– 1.sql、2.sql・・・の管理はプログラマがやる
– ミスした場合、エボリューション用のテーブルを元に戻すと
いう操作を手動でやらないといけない
– ある程度の規模に...
29
Slickの今後
30
次期バージョン2.1
• 使いやすさの向上
– APIの改善
– ドキュメントの充実
• Play2.4からデフォルトのO/Rマッパ(予定)
• Direct Embedding
– ケースクラスにアノテーションを付与
• スキーマ定義が...
Upcoming SlideShare
Loading in …5
×

Slick入門

3,629
-1

Published on

Published in: Software

Slick入門

  1. 1. 1 Slick入門 Takako Shimamoto BizReach, Inc.
  2. 2. 2 アジェンダ • Scalaの代表的なORM • Slickの基本的なお話 – スキーマ定義 – クエリ • SQLを直接書く方法 – Plain SQL – その他の選択肢 • Play2 + Slickで使うには – 設定 – コネクションまわり • プロジェクトでの留意点
  3. 3. 3 Scalaで使えるORM • Slick(旧ScalaQuery) – タイプセーフなDSLや、SQLも記述可能 – 極力Scalaのコレクションと同じように扱えるよう設計され ている • Squeryl – SQLは使用せず、DSLでクエリを記述する – コンセプトは、できるだけシンプルに val query = from(table) (t => where(t.id === id) select(t))
  4. 4. 4 Scalaで使えるORM • Anorm – SQLを直接書くスタイル – 手動でORマッピングを行う • Scala ActiveRecord – Squerylをベースに、モデル定義やよく使う機能をRailsの ActiveRecordに似せている(CoC、DRY) SQL("select * from User") .as( int("id") ~ str("name") map { case id~name => Person(id, name) } *)
  5. 5. 5 他にも、Activate、ScalikeJDBC、SORM など・・・ 本日はTypesafe社お墨付きの Slickについてお話します
  6. 6. 6 Slickのきほん
  7. 7. 7 タイプセーフなクエリ • Slickは以下のように、クエリをタイプセーフに記述で きることが1つの特徴 • このクエリを書くためには、スキーマ定義が必要 val id = 1L val user: Option[UsersRow] = Users.filter(_.id is id.bind).firstOption select * from USERS x1 where x1.ID = ? SQLのイメージ
  8. 8. 8 スキーマ定義 • でも、手動で書くのは面倒ですよね // マッピングするケースクラス case class UserRow(id: Long, name: String) // テーブルのスキーマ定義 class User(tag: Tag) extends Table[UserRow](tag, "USER") { def * = (id, name) <> (UserRow.tupled, UserRow.unapply) val id: Column[Long] = column[Long]("ID", O.PrimaryKey) val name: Column[String] = column[String]("NAME") } // クエリに使用 lazy val User = new TableQuery(tag => new User(tag))
  9. 9. 9 実はジェネレータがあるんですよ
  10. 10. 10 コードジェネレータ • sbtのタスクとして実行 • 生成するファイル – Tables.scala • 生成されるもの – テーブル定義 – マッピングするケースクラス – TableQuery • クエリで使用 – 全テーブルのDDL – GetResult(ResultSetからケースクラスに変換) • Plain SQLで使用
  11. 11. 11 コードジェネレータ • ジェネレータはカスタマイズ可能 – SourceCodeGeneratorを拡張する形 – ドキュメントがないので拡張ポイントがわかりにくい – slick.model.codegenパッケージ配下を見るとよい • BizReachではカスタマイズして使っている – 基本的なCRUD機能 – シールドクラスとカラムのマッピング – 楽観的排他による更新機能
  12. 12. 12 これでクエリが書けます
  13. 13. 13 基本的なCRUD // 全件取得 val users: Seq[UserRow] = User.list // 登録 val res: Int = User insert UserRow(1, "なまえ") // 更新 val res: Int = User.filter(_.id is id.bind).update( UserRow(1, "なまえ変更")) // 削除 val res: Int = User.filter(_.id is id.bind).delete
  14. 14. 14 基本的なCRUD // 全件取得 val users: Seq[UserRow] = User.list // 登録 val res: Int = User insert UserRow(1, "なまえ") // 更新 val res: Int = User.filter(_.id is id.bind).update( UserRow(1, "なまえ変更")) // 削除 val res: Int = User.filter(_.id is id.bind).delete bindを呼ぶと バインド変数になる
  15. 15. 15 バインド変数になるbind • bindなし • bindあり val name = "ta'kako" User.filter(_.name is name).firstOption ・・・ from USER x1 where x1.NAME = 'ta''kako' SQLのイメージ User.filter(_.name is name.bind).firstOption ・・・ from USER x1 where x1.NAME = ? SQLのイメージ
  16. 16. 16 ケースクラスにマッピング • <>を使えば、mapで絞り込んだ項目を別のケースク ラスにマッピングできる // このケースクラスにマッピング case class Test(id: Long, name: String) val res: List[Test] = User.map { t => t.id -> t.name <> (Test.tupled, Test.unapply) }.list
  17. 17. 17 他にもいろいろ • ソート、グルーピング、ページング など • 内部結合、外部結合も可能 • slick-referenceをGitHubに公開中 https://github.com/bizreach/slick-reference
  18. 18. 18 タイプセーフなクエリで書けない ときは?
  19. 19. 19 Plain SQL • SQLを文字列リテラルで書く • 局所的に使うには便利 • ただ、複雑なSQLの代替として使うには厳しい – リファクタリングがやりにくい – 動的なSQLを組み立てるためには文字列処理が必要 sql"""SELECT ID + 1 FROM ISSUE_ID WHERE USER_NAME = $owner FOR UPDATE """.as[Int].firstOption
  20. 20. 20 その他の選択肢 • mirage-scala – Seasar2の2WaySQLの機能を単独のライブラリとして利 用できるようにしたもの – Slickと組み合わせて使用することも可能 val books: List[Book] = sqlManager.getResultList[Book]( Sql(""" SELECT BOOK_ID, BOOK_NAME, AUTHOR, PRICE FROM BOOK /*IF author!=null*/ WHERE AUTHOR = /*author*/'test' /*END*/ """), Map("author"->"Takako Shimamoto")) パラメータはMapではなくケース クラスで与えることも可能 条件分岐やパラメータをSQLのコメント で記述するのでコピペしてそのままDB に流して動作確認できる 外部ファイル化することも可能
  21. 21. 21 Play2でSlickを使うには
  22. 22. 22 play-slick • Play2でSlickを使う上で、面倒なことをいろいろやっ てくれるプラグイン – Play2の設定から自動的にSlickの Database.forDataSourceを呼び出してくれる – Slickのセッション管理を自動的にやってくれる • withSessionやwithTransactionを呼び出してくれる – Play2のリクエストとSlickのセッションの両方を持ち合わ せたDBSessionRequestが使える SlickがPlay2に組み込まれるのと同時に このプラグインも統合される予定
  23. 23. 23 何が変わるの? • コントローラでPlay2のActionの代わりに、play-slick のDBActionを使う • トランザクションを開始する場合は DBAction.transactionを使う def list = DBAction { implicit rs => // IDの昇順にすべてのユーザ情報を取得 val users = Users.sortBy(t => t.id).list // 一覧画面を表示 Ok(views.html.user.list(users)) }
  24. 24. 24 コネクションプール • Slickはコネクションプールの実装を持っていない • Play2と組み合わせて使うならBoneCP • ただし、有名なリーク問題が未だにある – https://bugs.launchpad.net/bonecp/+bug/999114 • 対処方法は – BoneCPのバージョンを固定 – maxConnectionAgeを0にする • 現在は、HikariCPをお試し中
  25. 25. 25 プロジェクトで使う上で注意 することは?
  26. 26. 26 留意事項 • アグレッシブな機能追加/削除 – Scala全体の傾向(Slickも例外ではない) – 特にメジャーアップにはマイグレーションが必要 – 常にキャッチアップして対応していく必要がある • マニュアルがあまり豊富でない – 本家サイトには書いてないが、できることが色々ある – 慣れるまでに多少の時間が必要 – 充実させようとしている動きはある – BizReachで公開しているリファレンスをぜひ活用を
  27. 27. 27 留意事項 • 結合は発行されるSQLに注意 – 単一テーブルなら、①fiterで絞り込む、②mapで取得項目 を決める、③listやfirstOptionで実行、という流れ – 結合は、メソッドチェーンとfor式で発行するSQLが異なる // select ・・・ from (select * from A) s1 inner join (select * from B) s2 on ・・・ A.innerJoin(B).on(・・・) // select ・・・ from A s1, B s2 where (s1.column = s2.column) and ・・ for { t1 <- A t2 <- B if ・・・ } yield ・・・
  28. 28. 28 留意事項 • Play2のEvolutionsは実践には厳しい – 1.sql、2.sql・・・の管理はプログラマがやる – ミスした場合、エボリューション用のテーブルを元に戻すと いう操作を手動でやらないといけない – ある程度の規模になるとメンテが困難
  29. 29. 29 Slickの今後
  30. 30. 30 次期バージョン2.1 • 使いやすさの向上 – APIの改善 – ドキュメントの充実 • Play2.4からデフォルトのO/Rマッパ(予定) • Direct Embedding – ケースクラスにアノテーションを付与 • スキーマ定義が不要になる – 暗黙の型変換の代わりに、マクロを使用 • 実装するコード量が減る – 現在は実験的な機能として提供 • 今後に期待
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×