Flyway with Spring Boot でDBマイグレーションを自動化する

【求人のご案内】Javaアプリ開発エンジニア募集

よく訓練されたアップル信者、都元です。本連載もそろそろ話題が多岐に渡って来たので、第X回という運用をやめようかと思います。プロジェクトとしては順番にアップデートして成長して行きますが、そろそろ順序はどうでも良くなって来た感がありますので。

さて、システム開発をしていると大抵の場合必要になるのが、バージョンアップに伴うRDBのマイグレーションです。

私事で恐縮ですが、筆者はその昔JiemamyというRDBマイグレーションの仕組みをOSSで開発していました(残念ながら、現在はプロジェクトは完全にクローズしています)。正直Jiemamyはモノにすることができなかったのですが、現時点ではFlywayというツールに大きな可能性を感じており、筆者もプロダクション環境でこのツールを活用しています。

弊社中村による過去のご紹介記事Flywayで簡単DBマイグレーションも併せて御覧ください。

FlywayとSpring Bootの連携

Spring Bootのドキュメント 68.5.1 Execute Flyway database migrations on startup にあるように、Spring BootはFlywayとシームレスな連携を行います。どのくらいシームレスかって、まずはGitHubのdiffを御覧ください。

なんと依存ライブラリに flyway-core を追加し、src/main/resources/db/migration/ 内にSQLファイルを配置しただけです。SQLファイルの命名規則等は、Flywayのドキュメントのここを御覧ください。

これだけで何が起こるかというと、アプリケーションの起動時に、DBのマイグレーション(ここではテーブルの新規作成と初期データの投入)が起こります。マジです。

まさに黒魔術の真骨頂かもしれません。Spring Bootとはそういうフレームワークです。

試してみる berserker v10.0

とりあえずここまでのberserkerプロジェクトを実行して来た方は、ローカルのMySQLにberserkerデータベースがあり、その中にテーブルがあると思います。

これは思い切って DROP DATABASE berserker;CREATE DATABASE berserker; でクリアしてしまいましょう。その上でflywayタグをチェックアウトの上、bootRunタスクを実行してみてください。(DBを削除しないと、テーブルが有るところにさらにテーブルを作ろうとしてエラーになると思います。)

$ git clone https://github.com/classmethod-aws/berserker.git
$ cd berserker
$ git checkout 10.0
$ ./gradlew bootRun
:compileJava
:processResources
:classes
:findMainClass
:bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.3.3.RELEASE)

 (略)

2016/04/08 11:42:21.353 [main] INFO  o.f.c.internal.util.VersionPrinter:44 - Flyway 3.2.1 by Boxfuse
2016/04/08 11:42:21.625 [main] INFO  o.f.c.i.dbsupport.DbSupportFactory:44 - Database: jdbc:mysql://localhost:3306/berserker?useLegacyDatetimeCode=false&serverTimezone=Universal (MySQL 5.7)
2016/04/08 11:42:21.648 [main] INFO  o.f.core.internal.command.DbValidate:44 - Validated 1 migration (execution time 00:00.011s)
2016/04/08 11:42:21.685 [main] INFO  o.f.c.i.m.MetaDataTableImpl:44 - Creating Metadata table: `berserker`.`schema_version`
2016/04/08 11:42:21.825 [main] INFO  o.f.core.internal.command.DbMigrate:44 - Current version of schema `berserker`: << Empty Schema >>
2016/04/08 11:42:21.826 [main] INFO  o.f.core.internal.command.DbMigrate:44 - Migrating schema `berserker` to version 1 - Create initial tables
2016/04/08 11:42:21.954 [main] INFO  o.f.core.internal.command.DbMigrate:44 - Successfully applied 1 migration to schema `berserker` (execution time 00:00.270s).
2016/04/08 11:42:22.031 [main] INFO  o.s.j.e.a.AnnotationMBeanExporter:431 - Registering beans for JMX exposure on startup
2016/04/08 11:42:22.069 [main] INFO  o.a.coyote.http11.Http11NioProtocol:180 - Initializing ProtocolHandler ["http-nio-8080"]
2016/04/08 11:42:22.087 [main] INFO  o.a.coyote.http11.Http11NioProtocol:180 - Starting ProtocolHandler ["http-nio-8080"]
2016/04/08 11:42:22.109 [main] INFO  o.a.tomcat.util.net.NioSelectorPool:180 - Using a shared selector for servlet write/read
2016/04/08 11:42:22.129 [main] INFO  o.s.b.c.e.t.TomcatEmbeddedServletContainer:162 - Tomcat started on port(s): 8080 (http)
2016/04/08 11:42:22.138 [main] INFO  j.c.e.berserker.BerserkerApplication:57 - Started BerserkerApplication in 6.267 seconds (JVM running for 7.317)

(略)直後のログから、Flywayが自動的に動いているのが分かると思います。localhostのDBに対して、schema_versionというテーブルを作っています。このテーブルで、現状どこまでのスキーマ定義が適用されているのかを管理しています。初回実行時にはこのテーブルは空ですので、バージョン1から順番にSQLを適用していきます。現状バージョン1しかありませんので、そのSQLファイルが1つ実行されるだけですが。

この結果、別のシェルから下記のように、DBにアクセスできていることが確認できます。

$ curl http://localhost:8080/
User(username=miyamoto, password=$2a$10$cPnF0sq.bCPHeGuzVagOgOmbe2spT1Uh1k9LyuS0jzb5F3Lm.9kEy),User(username=yokota, password=$2a$10$nkvNPCb3Y1z/GSearD7s7OBdS9BoLBss3D4enbFQIgNJDvr0Xincm)