Play Framework(Scala) で、play-JDBC と play-slick を使って DBアクセス
はじめに
はじめまして!
ネクストビートに新卒入社した井上と申します。
ネクストビートには「前倒し入社」という制度があり、通常は4月入社ですが7ヶ月前倒しで21年の9月に入社しました。(今となっては前倒し入社してよかったと思いますが、内定を頂いて「前倒し入社しませんか?」と言われた時は、人事の方が何を言っているのかわからず戸惑いました。笑)
記事に関して
ネクストビートの新卒には研修期間が設けられており、Scala、Play Framework、Angular を中心に、アプリケーションを作りながら言語やフレームワークの学習をしていきます。
研修中は、ネクストビートのアプリケーションを実装する際に使われている IxiaS を使って DB にアクセスします。その為、IxiaS 内で使われている Slickに触れる機会は多くありませんでした。
そこで今回は、Play Framework(Scala) で play-JDBC、play-slick を使い自力で DB アクセスをしてみようと思います!
前提
DB は構築済みです。Docker にMySQL を構築しています。
・DB 名:kidsna_db
・Table名:user
・Table構造
JDBC とは?
Java言語でデータベースにアクセスするためのAPIです。
JDBCの利用方法は DB製品には依存せず、DB製品毎の差異は JDBCドライバが吸収します。
JDBC ドライバは、JDBC の仕様に基づき実装された Java のプログラムで、DB 接続や SQL の発行を行います。DB 製品の種類に応じて、DB ベンダが提供したものを利用します。
今回は Play Framework を採用しているため、MySQL 用の play-JDBC を使用します。
play-JDBC で DB アクセス
play-JDBCを設定
build.sbt にプロジェクトのビルド定義を記述します。
application.conf にアプリケーションの設定を記述します。
モデル定義
DB の user テーブルに倣って User モデルを定義する
コントローラーで DB アクセスして、データを取得
DB アクセスする際の基本的な流れ
- DB とのコネクションを作成(DB との接続)
- ステートメントを作成・実行(SQL 文を発行)
- 実行結果から値を取得
具体的な処理の流れ
- コントローラーに Database トレイトを DI する(実際に使われているのは具体的な実装がされている、PooledDatabase)
- withConnection でコネクションを作成し、{ Connection => A } の処理を渡す
- createStatement で、ステートメントを作成
- executeQuery で SQL 文を実行し、ResultSet(実行結果)を取得
- while の処理について
・ResultSet.next では、実行結果を1行ずつ呼び出し、行がある場合は、true、行がなくなった場合は false を返す
・getInt, getString など、get の後に続く文字の型でカラムを取ってくる(getInt → Int 型、getString → String 型)
・User モデルを生成し、List[User] に追加する
テンプレートに表示
- テンプレートに表示してみて、以下のことに気が付きました。
getInt → カラムが NULL の場合、0を取得
getString → カラムが NULL の場合、null を取得(空の文字列かと思ったら null だった)
- 趣旨とはそれるが twirl は、Option[T] に、get しなくても T で表示される
Option[Option[T]] などでも、T が表示される
play-JDBC を使ってみて
- play-JDBC は、DBアクセスからデータの取得まで、処理の流れがわかりやすかった
- getIntしたカラムの値が NULL 時に 0 が返ってくるのは、カラムの値が0なのか NULL なのかわからないためやりづらそう
( getLong, getByte なども NULL 時に0が返る)
Slick とは?
リレーショナルデータベースでの作業を容易にするScala用のTypesafeのFunctional Relational Mapping(FRM)ライブラリです。
Scalaコレクションを使用しているかのように保存されたデータを操作できると同時に、データベースアクセスが発生するタイミングと転送されるデータを完全に制御できます。
SQLを直接使用することもできます。
今回は、Play Framework の、play-slick を使っていきます。
play-slick で DB アクセス
play-slick では、DB のテーブルとモデルを紐付ける Table 定義と、DB アクションをする Repository の定義をします。
コントローラーでは、Repository を使い処理をします。
play-slick を設定
- build.sbt
”com.typesafe.play” %% “play-slick” % “3.0.3” を追加します。
Table 定義
Table 定義は、モデルとの紐付けの役割を担います。
- Table を継承した、クラスの定義(UserTable)
Table は、AbstractTable を継承しているため、* メソッドの実装を強制される(SQL 文の “ SELECT * FROM [Table]にあたる)
* メソッドは、DB アクセスの際にデフォルトで使われる
* メソッドや、Repository での処理でカラム単位の操作をするために、カラムに対しても、column メソッドを実装する - コンパニオンオブジェクトで、TableQuery のインスタンスを生成(これを使って DB アクセスを行う)
Repository 定義
Repository 定義は、実際の DB へのアクションを定義します。
- まず DB の urlなどを設定(url 以外は、引数で必須になっていなかったが、driver 等も指定してやらないと DB アクセスできなかった)
- db: DatabaseDef の、run メソッド内で、TableQuery に対してメソッドを使ってデータを取得する(result メソッドで、DBAction が返る)
今回は、全てのデータを取得するため、TableQuery.result としている(デフォルトの * メソッドのみが実行される)
コントローラーの処理
今回は、Repository で、DB の設定を記述しているため、JDBC のようにapplication.conf にDB の設定を記述し、 DI するという実装ではありません。
テンプレート表示(play-JDBC の時と処理は同じ)
play-JDBC の時との違い
- カラムが NULL の時、Scala の None を返すことができる
play-slick を使ってみて
- Table, Repository, Controller の役割が明確に分担されているため、Table 定義などの変更にも対応しやすいと感じた
- Controller の記述が play-JDBC に比べかなりスッキリする
- SQL 文を直接書く必要がない
- DB の カラム が NULL の場合は、Scala の None になるため扱いやすい
おわりに
2つの技術に触れてみて感じた事として、同じことをしていても違う良さがあると思いました。
play-JDBC は、DB アクセスするまでの学習コストが低く、使いやすさを感じました。
play-slick は、学習コストが play-JDBC に比べて上がる代わりに、SQL 文を書いたことない人でも Scala のコードで DB アクセスが可能な点、コントローラーの記述がかなりスッキリする点、Scala の型として扱える点が良い点だと思いました。
また機会があれば勉強したことをブログにしたいと思います。
最後まで読んでいただきありがとうございました。
参考文献
We are hiring!
「人口減少社会において必要とされるインターネット事業を創造し、ニッポンを元気にする。」
を理念に掲げ一緒に働く仲間を募集しております。