slick入門
TRANSCRIPT
![Page 1: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/1.jpg)
1
Slick入門
Takako Shimamoto
BizReach, Inc.
![Page 2: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/2.jpg)
2
アジェンダ
• Scalaの代表的なORM
• Slickの基本的なお話
– スキーマ定義
– クエリ
• SQLを直接書く方法
– Plain SQL
– その他の選択肢
• Play2 + Slickで使うには
– 設定
– コネクションまわり
• プロジェクトでの留意点
![Page 3: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/3.jpg)
3
Scalaで使えるORM
• Slick(旧ScalaQuery)
– タイプセーフなDSLや、SQLも記述可能
– 極力Scalaのコレクションと同じように扱えるよう設計されている
• Squeryl
– SQLは使用せず、DSLでクエリを記述する
– コンセプトは、できるだけシンプルに
val query =
from(table)
(t => where(t.id === id) select(t))
![Page 4: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/4.jpg)
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)
} *)
![Page 5: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/5.jpg)
5
他にも、Activate、ScalikeJDBC、SORM など・・・
本日はTypesafe社お墨付きの
Slickについてお話します
![Page 6: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/6.jpg)
6
Slickのきほん
![Page 7: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/7.jpg)
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のイメージ
![Page 8: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/8.jpg)
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))
![Page 9: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/9.jpg)
9
実はジェネレータがあるんですよ
![Page 10: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/10.jpg)
10
コードジェネレータ
• sbtのタスクとして実行
• 生成するファイル
– Tables.scala
• 生成されるもの
– テーブル定義
– マッピングするケースクラス
– TableQuery
• クエリで使用
– 全テーブルのDDL
– GetResult(ResultSetからケースクラスに変換)• Plain SQLで使用
![Page 11: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/11.jpg)
11
コードジェネレータ
• ジェネレータはカスタマイズ可能
– SourceCodeGeneratorを拡張する形
– ドキュメントがないので拡張ポイントがわかりにくい
– slick.model.codegenパッケージ配下を見るとよい
• BizReachではカスタマイズして使っている
– 基本的なCRUD機能
– シールドクラスとカラムのマッピング
– 楽観的排他による更新機能
![Page 12: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/12.jpg)
12
これでクエリが書けます
![Page 13: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/13.jpg)
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
![Page 14: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/14.jpg)
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を呼ぶとバインド変数になる
![Page 15: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/15.jpg)
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のイメージ
![Page 16: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/16.jpg)
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
![Page 17: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/17.jpg)
17
他にもいろいろ
• ソート、グルーピング、ページング など
• 内部結合、外部結合も可能
• slick-referenceをGitHubに公開中
https://github.com/bizreach/slick-reference
![Page 18: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/18.jpg)
18
タイプセーフなクエリで書けないときは?
![Page 19: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/19.jpg)
19
Plain SQL
• SQLを文字列リテラルで書く
• 局所的に使うには便利
• ただ、複雑なSQLの代替として使うには厳しい
– リファクタリングがやりにくい
– 動的なSQLを組み立てるためには文字列処理が必要
sql"""SELECT ID + 1
FROM ISSUE_ID
WHERE USER_NAME = $owner FOR UPDATE
""".as[Int].firstOption
![Page 20: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/20.jpg)
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
に流して動作確認できる外部ファイル化することも可能
![Page 21: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/21.jpg)
21
Play2でSlickを使うには
![Page 22: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/22.jpg)
22
play-slick
• Play2でSlickを使う上で、面倒なことをいろいろやってくれるプラグイン
– Play2の設定から自動的にSlickのDatabase.forDataSourceを呼び出してくれる
– Slickのセッション管理を自動的にやってくれる
• withSessionやwithTransactionを呼び出してくれる
– Play2のリクエストとSlickのセッションの両方を持ち合わせたDBSessionRequestが使える
SlickがPlay2に組み込まれるのと同時にこのプラグインも統合される予定
![Page 23: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/23.jpg)
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))
}
![Page 24: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/24.jpg)
24
コネクションプール
• Slickはコネクションプールの実装を持っていない
• Play2と組み合わせて使うならBoneCP
• ただし、有名なリーク問題が未だにある
– https://bugs.launchpad.net/bonecp/+bug/999114
• 対処方法は
– BoneCPのバージョンを固定
– maxConnectionAgeを0にする
• 現在は、HikariCPをお試し中
![Page 25: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/25.jpg)
25
プロジェクトで使う上で注意することは?
![Page 26: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/26.jpg)
26
留意事項
• アグレッシブな機能追加/削除
– Scala全体の傾向(Slickも例外ではない)
– 特にメジャーアップにはマイグレーションが必要
– 常にキャッチアップして対応していく必要がある
• マニュアルがあまり豊富でない
– 本家サイトには書いてないが、できることが色々ある
– 慣れるまでに多少の時間が必要
– 充実させようとしている動きはある
– BizReachで公開しているリファレンスをぜひ活用を
![Page 27: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/27.jpg)
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 ・・・
![Page 28: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/28.jpg)
28
留意事項
• Play2のEvolutionsは実践には厳しい
– 1.sql、2.sql・・・の管理はプログラマがやる
– ミスした場合、エボリューション用のテーブルを元に戻すという操作を手動でやらないといけない
– ある程度の規模になるとメンテが困難
![Page 29: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/29.jpg)
29
Slickの今後
![Page 30: Slick入門](https://reader035.vdocuments.pub/reader035/viewer/2022070321/558ff4fb1a28abf6798b457c/html5/thumbnails/30.jpg)
30
次期バージョン2.1
• 使いやすさの向上
– APIの改善
– ドキュメントの充実
• Play2.4からデフォルトのO/Rマッパ(予定)
• Direct Embedding
– ケースクラスにアノテーションを付与• スキーマ定義が不要になる
– 暗黙の型変換の代わりに、マクロを使用• 実装するコード量が減る
– 現在は実験的な機能として提供• 今後に期待