冥冥乃志

ソフトウェア開発会社でチームマネージャをしているエンジニアの雑記。アウトプットは少なめです。

follow us in feedly

Spring Bootでローカル環境と本番環境で異なるDBMSを使う

Spring Bootでプロファイルごとに異なるDBMSを使うように接続情報を分ける、プロファイルごとのhibernateの初期設定を変更する、ということをやったので、備忘録的にメモ。

背景

既存にある別のプロダクトのデータを取得するREST APIを作り始めたのですが、プロダクトが利用するDBMSSQLServerで、既にデータベース自体は構築されています。また、私の開発環境はMacなのですが、テストコード動かしたりローカルで動かしたりする場合にいちいちサーバ用意したりするのも何か手間です。

そのため、ローカルで開発している段階では、h2辺りをインメモリで使えるようにしておきたかったというのが主な理由です。ただし、デプロイ時に設定ファイルとか構成をごにょごにょしてしまうのも事故の元なのでなるべく避けたい。環境を指定して実行すれば、勝手に切り分けてくれる、というのを今回のゴールにしました。

application.ymlでプロファイルを分ける

まず、実行環境ごとに接続するDBMSを分けます。application.ymlに実行環境ごとのプロファイルを指定して、接続先と利用するjdbcドライバのクラス名を指定します。これで、両環境で使うjdbcドライバがあれば、実行プロファイルごとに異なるDBMSを利用できます。以下がサンプルです。

spring:
  profiles:
    active: dev

---
spring:
  profiles: dev
  datasource:
    url: jdbc:h2:mem:test;MODE=MSSQLServer;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password: 
    driverClassName: org.h2.Driver

---
spring:
  profiles: production
  datasource:
    url: jdbc:sqlserver://[本番環境のアクセス情報]
    username: [ユーザ名]
    password: com.microsoft.sqlserver.jdbc.SQLServer
    initialize: false

ローカル環境用のプロファイルはdevという名前で、デフォルトで実行されます。また、h2をインメモリモードで使うようにしています。

本番環境用のプロファイルはproductionという名前です。

こうして設定したプロファイルは、jarファイルの実行時に以下のようにパラメータで指定します。

java -jar -Dspring.profiles.active=production hoge.jar

初期データの扱い

ローカル環境では、インメモリでデータベースを扱うため、テスト用にもローカル実行用にも初期データが必要です。Spring Bootはクラスパス上に data.sql があると初期実行SQLとしてアプリケーション起動時に実行してくれます。楽なので、ローカル環境での初期データ投入は、ぜひとも使いたい機能です。しかし、本番環境では既に出来上がっているデータを対象とするので、 data.sql は実行してほしくありません。

そのために、 application.yml で本番環境のプロファイルのデータソースに initialize: false を指定しています。

起動ログを見比べてみます(名前など一部手を加えています)。まずはh2を使っているdev環境。

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

2015-07-14 13:20:06.003  INFO 1380 --- [           main] sample.Application                 : Starting Application on abeshinsuke-no-MacBook-Pro.local with PID 1380 ([プロジェクトのパス]/build/classes/main started by [ユーザ名] in [プロジェクトのパス])
〜〜中略〜〜
2015-07-14 13:20:11.348  INFO 1380 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core {4.3.10.Final}
2015-07-14 13:20:11.350  INFO 1380 --- [           main] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2015-07-14 13:20:11.359  INFO 1380 --- [           main] org.hibernate.cfg.Environment            : HHH000021: Bytecode provider name : javassist
2015-07-14 13:20:11.665  INFO 1380 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {4.0.5.Final}
2015-07-14 13:20:11.856  INFO 1380 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2015-07-14 13:20:12.094  INFO 1380 --- [           main] o.h.h.i.ast.ASTQueryTranslatorFactory    : HHH000397: Using ASTQueryTranslatorFactory
2015-07-14 13:20:12.898  INFO 1380 --- [           main] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000227: Running hbm2ddl schema export
2015-07-14 13:20:12.912  INFO 1380 --- [           main] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000230: Schema export complete
2015-07-14 13:20:13.052  INFO 1380 --- [           main] o.s.jdbc.datasource.init.ScriptUtils     : Executing SQL script from URL [file:[プロジェクトのパス]/src/main/resources/data.sql]
2015-07-14 13:20:13.075  INFO 1380 --- [           main] o.s.jdbc.datasource.init.ScriptUtils     : Executed SQL script from URL [file:[プロジェクトのパス]/src/main/resources/data.sql] in 22 ms.
〜〜中略〜〜
2015-07-14 13:20:13.845  INFO 1380 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-07-14 13:20:13.848  INFO 1380 --- [           main] sample.Application                 : Started Application in 8.208 seconds (JVM running for 8.726)

次に、SQLServerに接続するproduction環境です。

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

2015-07-14 13:17:44.158  INFO 1350 --- [           main] sample.Application                 : Starting Application on abeshinsuke-no-MacBook-Pro.local with PID 1350 ([プロジェクトのパス]/build/libs/hoge.jar started by shinsuke-abe in [プロジェクトのパス])
〜〜中略〜〜
2015-07-14 13:17:49.651  INFO 1350 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core {4.3.10.Final}
2015-07-14 13:17:49.654  INFO 1350 --- [           main] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2015-07-14 13:17:49.656  INFO 1350 --- [           main] org.hibernate.cfg.Environment            : HHH000021: Bytecode provider name : javassist
2015-07-14 13:17:49.938  INFO 1350 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {4.0.5.Final}
2015-07-14 13:17:50.197  INFO 1350 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.SQLServer2012Dialect
2015-07-14 13:17:50.331  INFO 1350 --- [           main] o.h.h.i.ast.ASTQueryTranslatorFactory    : HHH000397: Using ASTQueryTranslatorFactory
〜〜中略〜〜
2015-07-14 13:17:51.756  INFO 1350 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-07-14 13:17:51.758  INFO 1350 --- [           main] sample.Application                 : Started Application in 8.077 seconds (JVM running for 8.646)

ログの違いでおわかりいただける通り、devプロファイルではEntityからの動的DDL生成と実行、それからdata.sqlを実行しての初期データセットが行われていますが、productionプロファイルでは行われていません。

h2でCompatibility Modeでの初期データロード(失敗)

まあ、上記の様な環境の構成で気になるのはDBMSの違い、ですよね。アプリケーション自体は簡単な参照がほとんどなので、クエリ自体はJPAとかJpaRepositoryに吸収してもらいますが、なるべく保険をかけておきたい所です。

で、調べてみた所、どうもh2にはCompatibility Modeというのがあり、SQLServerのモードも実装はされている模様 *1 。実行するSQLのカラムなどの識別子に各括弧([])がつくとか、テキストの連結を+でするとか、SQLServerの仕様に寄せたSQLを実行できるようです。

これがですねー、うまく動かないわけですよ。接続文字列にモードを指定して、SQLServerに寄せたINSERT文を data.sql に記載して動かしてみたんですが、スキーマが見つからない。。。結局 data.sql で初期データをロードするSQLは通常のINSERT文にして実行したらうまく行きました *2

まとめ

  • プロファイルの切り替え楽
  • h2のCompatibility Mode良くわからん
  • 実行時のプロファイル指定で事故りそうなのでなんか考える

追記

実行時のプロファイル指定については、環境変数指定が一番事故が少なくなりそうです。上記の application.yml のままで、実行環境で SPRING_PROFILES_ACTIVE=production環境変数をセットして実行します。

Spring Bootでは設定ファイルよりも環境変数が優先されるので、本番サーバでは環境変数を指定しておいた方が良いのではないかと思います。

*1:http://www.h2database.com/html/features.html#compatibility

*2:それはそれで挙動としては微妙ですが。。。