Spring Bootでローカル環境と本番環境で異なるDBMSを使う
Spring Bootでプロファイルごとに異なるDBMSを使うように接続情報を分ける、プロファイルごとのhibernateの初期設定を変更する、ということをやったので、備忘録的にメモ。
背景
既存にある別のプロダクトのデータを取得するREST APIを作り始めたのですが、プロダクトが利用するDBMSはSQLServerで、既にデータベース自体は構築されています。また、私の開発環境は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:それはそれで挙動としては微妙ですが。。。