Play 2.3.xから2.4.x、Slick 2.xから3.1.xへの移行

Play Java 歴 1 年くらいの私が、最近、プロジェクトで、Play Scala、Slickのバージョンアップを行いました。マイグレーションを行う方が、事前に知っておいた方が良いことについて記しておきます。なお、ここに記載されていることが全てではありませんので、マイグレーションガイドを参照ください。

# Play 2.4.x への以降

移行の詳細については、[マイグレーションガイド](https://www.playframework.com/documentation/2.4.4/Migration24)を参照。

## ビルド方法の変更

project/plugins.sbt にあるPlayを以下に更新します。

```
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.0")
```

Play 2.4 の対応 sbt バージョンは 0.13.8 です。project/build.properties を更新しましょう。

```
sbt.version=0.13.8
```

## Javaは8のみサポート

6と7はサポート対象外になり、6または7でビルドしようとするとエラーになるようです。

## Dependency Injectionのサポート

3.0 でグローバルな状態管理を完全に廃止することを目標にDIの導入を行っているようです。2.4から [Guice](https://github.com/google/guice)というライブラリを使ったDIをデフォルトでサポートし始めたようです。DIの詳細は[ドキュメント](https://www.playframework.com/documentation/2.4.4/ScalaDependencyInjection)を参照。

対応する場合、まずbuild.sbtにルートジェネレータとして、DIに対応版を使うことを明記。

```
routesGenerator := InjectedRoutesGenerator
```

DI 対応の目印で conf/routes の Controller のメソッド名に @ をつけます。

```
GET        /some/path           @controllers.Application.index
```

* DIのを使うことは @Inject で明記します。@inject は [JSR330](https://jcp.org/en/jsr/detail?id=330) で決められた DI 用のアノテーション。
* 共通に DI を使う用途は、後述のメッセージ国際化対応（messagesApi: MessagesApi）、DB の設定（dbConfigProvider: DatabaseConfigProvider）です。

```
class Application @Inject() (dbConfigProvider: DatabaseConfigProvider, messagesApi: MessagesApi) extends Controller with I18nSupport {
```

なお、コントローラを object（シングルトンオブジェクト）としていた場合、今後はclassにし、かつ @Singleton をつけることで、引き続きシングルトンとして扱うことが可能です。

```
@Singleton
class YoutSingletonClass @Inject(yourParameter: YourClass) {
```

## 国際化APIへの対応

[Internationalization API](https://www.playframework.com/documentation/2.4.x/ScalaI18N) が導入されました。ビューに表示するメッセージは言語ごとに設定ファイルとして用意し、 play.api.i18n.Messages としてコントローラやビューに渡すようになりました。詳細はドキュメント参照。

DIの仕組みでviewに注入するため、viewの先頭にmessageの宣言が必要です。

```
(implicit messages: Messages)
```

Controllerも messagesApi: play.api.i18n.Messages を渡すように修正が必要。

```
class Application @Inject() (dbConfigProvider: DatabaseConfigProvider, messagesApi: MessagesApi) extends Controller with I18nSupport {
```

また、conf/application.confのアプリケーション言語設定もi18nの方を使うように修正。

```
play.i18n.langs = [ "en" ]
```

# Slick 3.0 への移行

詳細は [Slick のマイグレーションガイド](https://www.playframework.com/documentation/2.4.4/PlaySlickMigrationGuide)を参照。

## buid.sbtの変更

* play-slick のバージョンを変更します。
* evolutions は別モジュールに切りだされたそうです。利用する場合は "evolutions"（ORM 共通）と "play-slick-evolutions"（Slickのevolutions）が必要です。

```
libraryDependencies ++= Seq(
  ...
  evolutions,
  "com.typesafe.play" % "play-slick_2.11" % "1.1.1",
  "com.typesafe.play" %% "play-slick-evolutions" % "1.1.1",
  ...
)
```

evolutiuonを止めたい場合は、conf/application.confに以下を設定。プロパティ名が変更されている点に注意。

```
play.evolutions.enabled=false
```

## DBの設定

slick を使う場合、application.conf の設定名が変更。プロパティ名が変更されている点に注意。

```
slick.dbs.default.driver="slick.driver.H2Driver$"
slick.dbs.default.db.driver=org.h2.Driver
slick.dbs.default.db.url="jdbc:h2:mem:play"
slick.dbs.default.db.user=sa
slick.dbs.default.db.password=""
```

## Slick ドライバの自動検出

これまでは以下の import で特定のドライバ用のAPIをインポートしていましたが、この方法は廃止されました。

```
import play.api.db.slick.Config.driver.simple._.
```

これからは application.conf で設定したものをフレームワークが自動検出し、DI で注入するようになりました。import driver.api._ の箇所で設定したドライバの API が利用できるようになります。

```
class Application @Inject() (dbConfigProvider: DatabaseConfigProvider, messagesApi: MessagesApi) extends Controller with I18nSupport {
  val dbConfig = dbConfigProvider.get[JdbcProfile]

  import dbConfig._
  import driver.api._
```

## 非同期クエリ実行のサポート

2.x系で使われていたDBAction、DBSessionRequest、DB.withSessionなどのAPIが廃止されました。

https://www.playframework.com/documentation/2.4.4/PlaySlick#Running-a-database-query-in-a-Controller

このドキュメントにあるように今後は、db.runというAPIを使います。これは Future を返すメソッドです。DB のクエリが非同期化され、コントローラのレスポンス部分も非同期化（Futureを返す）ようになっている点に注目。
例えば、あるまとまった処理を小分けてし、非同期処理し、結果がまとまったら返すみたいなことができ、スケールアウトさせることが容易になったといえます。

```
def index(name: String) = Action.async { implicit request =>
  val resultingUsers: Future[Seq[User]] = dbConfig.db.run(Users.filter(_.name === name).result)
  resultingUsers.map(users => Ok(views.html.index(users)))
}
```

以上です。本記事が、これからマイグレーションを行う方にとって参考になりましたら、幸いです。もし記事の内容で、気になる点、間違いなどありましたら、コメントいただけると嬉しいです。