Play Framework(Scala) で、play-JDBC と play-slick を使って DBアクセス

Kanta Inoue
nextbeat-engineering
8 min readJun 20, 2022

はじめに

はじめまして!

ネクストビートに新卒入社した井上と申します。

ネクストビートには「前倒し入社」という制度があり、通常は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 アクセスする際の基本的な流れ

  1. DB とのコネクションを作成(DB との接続)
  2. ステートメントを作成・実行(SQL 文を発行)
  3. 実行結果から値を取得

具体的な処理の流れ

  1. コントローラーに Database トレイトを DI する(実際に使われているのは具体的な実装がされている、PooledDatabase
  2. withConnection でコネクションを作成し、{ Connection => A } の処理を渡す
  3. createStatement で、ステートメントを作成
  4. executeQuery で SQL 文を実行し、ResultSet(実行結果)を取得
  5. 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 定義は、モデルとの紐付けの役割を担います。

  1. Table を継承した、クラスの定義(UserTable)
    Table は、AbstractTable を継承しているため、* メソッドの実装を強制される(SQL 文の “ SELECT * FROM [Table]にあたる)
    * メソッドは、DB アクセスの際にデフォルトで使われる
    * メソッドや、Repository での処理でカラム単位の操作をするために、カラムに対しても、column メソッドを実装する
  2. コンパニオンオブジェクトで、TableQuery のインスタンスを生成(これを使って DB アクセスを行う)

Repository 定義

Repository 定義は、実際の DB へのアクションを定義します。

  1. まず DB の urlなどを設定(url 以外は、引数で必須になっていなかったが、driver 等も指定してやらないと DB アクセスできなかった)
  2. 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!

株式会社ネクストビートでは

「人口減少社会において必要とされるインターネット事業を創造し、ニッポンを元気にする。」
を理念に掲げ一緒に働く仲間を募集しております。

https://www.nextbeat.co.jp/recruit

--

--