まえがき
SpringBootでFlywayを利用する方法は3つある。
・FlywayAutoConfiguration経由
・Java API経由
・Mavenプラグイン経由
本記事では3つの設定方法を備忘録として残しておくものである。
FlywayAutoConfiguration経由
application.propertiesに必要なプロパティと、pom.xmlに依存性を定義するだけで、
SpringBootアプリ起動時にmigrationを行うことができる。
後述するJava API、Mavenプラグインの方法と比べて、設定するものが少ないのですぐ使うには一番良い方法。
application.properties
# DB
spring.datasource.username=${db_username}
spring.datasource.password=${db_password}
spring.datasource.url=jdbc:postgresql://${host}:${port}/${database}
# Flyway
spring.flyway.enabled=true
spring.flyway.user=${db_username}
spring.flyway.password=${db_password}
spring.flyway.url=${spring.datasource.url}
pom.xml
<depedencies>
<!-- DBドライバ -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.3</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.17</version>
</dependency>
<!--Flyway -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>8.5.4</version>
</dependency>
</depedencies>
ハマったところ
この方法で正常動作するまでにハマったポイントがいくつかあったので、備忘録として残しておく。
spring.flyway.enabled=true設定してもmigrationが実行されない。なんで?
ぐぐるとSpringBoot×Flywayの記事が沢山出てくるが、「sping.flyway.enabled=trueを設定すればSpringBoot起動時にmigration動くで」くらいしか書いてなくて「いや、動かんわ(´_ゝ`)」となった。
↑のStackOverFlowにあるように、こういうときはログを出そう。
ということでapplication.propertiesにlogging.level.root=debug
を追加してもう一回SpringBoot起動すると、↓のログが出力された。どうやら設定が足りていないらしい。
FlywayAutoConfiguration:
Did not match:
- AnyNestedCondition 0 matched 2 did not; NestedCondition on FlywayAutoConfiguration.FlywayDataSourceCondition.FlywayUrlCondition @ConditionalOnProperty (spring.flyway.url) did not find property 'url'; NestedCondition on FlywayAutoConfiguration.FlywayDataSourceCondition.DataSourceBeanCondition @ConditionalOnBean (types: javax.sql.DataSource; SearchStrategy: all) did not find any beans of type javax.sql.DataSource (FlywayAutoConfiguration.FlywayDataSourceCondition)
Matched:
- @ConditionalOnClass found required class 'org.flywaydb.core.Flyway' (OnClassCondition)
- @ConditionalOnProperty (spring.flyway.enabled) matched (OnPropertyCondition)
FlywayAutoConfiguration.FlywayConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.springframework.jdbc.support.JdbcUtils' (OnClassCondition)
- Ancestor org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration did not match (ConditionEvaluationReport.AncestorsMatchedCondition)
FlywayAutoConfigurationクラスを有効にするための条件がいくつかあり、↑のログに出ている「Did not match」のところを解消すればいけそう。
足りてないやつその1
@ConditionalOnClass did not find required class 'org.springframework.jdbc.support.JdbcUtils'
クラスパスにJdbcUtilsクラスがないということで、JdbcUtilsクラスを含む依存性を追加してあげる。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.17</version>
</dependency>
足りてないやつその2
AnyNestedCondition 0 matched 2 did not; NestedCondition on FlywayAutoConfiguration.FlywayDataSourceCondition.FlywayUrlCondition @ConditionalOnProperty (spring.flyway.url) did not find property 'url';
application.propertiesにspring.flyway.url
を追加してあげる。
# DB
spring.datasource.username=${db_username}
spring.datasource.password=${db_password}
spring.datasource.url=jdbc:postgresql://${host}:${port}/${database}
# Flyway
spring.flyway.enabled=true
spring.flyway.url=${spring.datasource.url} ←追加
再度SpringBootを起動。しかし失敗。まだ何か足りていないようだ。
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.internal.exception.FlywaySqlException: Unable to obtain connection from database: The server requested SCRAM-based authentication, but the password is an empty string.
--------------------------------------------------------------------------------------------------------------------------------
SQL State : 08004
Error Code : 0
Message : The server requested SCRAM-based authentication, but the password is an empty string.
どうやらFlywayAutoConfigureクラスは有効化できたようで、FlywayInitializerのBean生成時にエラーが発生している。「the password is an empty string」って怒られているので、spring.flyway.password
をapplication.propertiesに追加してあげる。(あとspring.flyway.userも)
# DB
spring.datasource.username=${db_username}
spring.datasource.password=${db_password}
spring.datasource.url=jdbc:postgresql://${host}:${port}/${database}
# Flyway
spring.flyway.enabled=true
spring.flyway.user=${db_username} ←追加
spring.flyway.password=${db_password} ←追加
spring.flyway.url=${spring.datasource.url}
これでmvn spring-boot:run
実行時に動くようになった!
AutoConfigureが動作する仕組み
先述したFlywayAutoConfigurationクラスを有効にするための条件がいくつかあり
の部分だが、SpringBootはこんな感じで「この条件が満たされていたら、HogeAutoConfigurationを有効化する」みたいなのが沢山ある。↓の記事に分かりやすくまとまっている。
FlywayAutoConfigurationはそのうちの1つである。各AutoConfigurationクラスを有効化する条件はSpringBootソースコードを見に行けば分かる。FlywayAutoConfigurationだと↓のように、クラス上部にある@Conditionalらへんが条件にあたる。
@AutoConfiguration(after = { DataSourceAutoConfiguration.class, JdbcTemplateAutoConfiguration.class,
HibernateJpaAutoConfiguration.class })
@ConditionalOnClass(Flyway.class)
@Conditional(FlywayDataSourceCondition.class)
@ConditionalOnProperty(prefix = "spring.flyway", name = "enabled", matchIfMissing = true)
@Import(DatabaseInitializationDependencyConfigurer.class)
public class FlywayAutoConfiguration {
Java API経由
pom.xml
<!-- Flyway Java API -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>8.0.1</version>
</dependency>
<!-- Flyway Java APIで使うDBドライバ -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.23</version>
</dependency>
Mainクラス
public static void main(String[] args) {
// appliacation.propertiesに外だしした方がgood
String url = "jdbc:postgresql://localhost:5433/sampleDatabase";
String user = "postgres";
String password = "postgres";
Flyway flyway = Flyway.configure().dataSource(url, user, password).load();
flyway.migrate();
}
SQLファイル用意(migrateで実行されるSQL)
・【src/main/resources/db/migration】配下にSQLファイル配置
・ファイル名は「V{数字}__HogeHoge.sql」
※アンダースコアは2つ。1つだけだと✕
# V1 → V2の順番で実行される
src/main/resources/db/migration/V1__Initialize.sql(テーブル定義用)
src/main/resources/db/migration/V2__insertData.sql(データ投入用)
プロファイルでSQLファイルを分ける。
application.properties
spring.flyway.locations=classpath:/common/db/migration,classpath:/dev/db/migration
SQLファイル
src/main/resources/common
・V1__Initialize.sql
src/main/resources/dev
・V2__TestData.sql
本番環境ではV1__Initialize.sqlだけ実行したい場合は、プロパティ「spring.flyway.locations」をビルド時に切り替えればOK。
Mainクラス実行
・MavenコマンドでJavaクラスを実行させたい場合、exec-maven-plugin経由でMainクラスを実行させる
実行コマンド:mvn exec:java
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>com.example.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
Mavenプラグイン経由
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>8.0.1</version>
<!-- DB接続情報 -->
<configuration>
<url>jdbc:postgresql://localhost:5433/sampleDatabase</url>
<user>postgres</user>
<password>postgres</password>
</configuration>
<!-- 必要なDBドライバを定義する -->
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.23</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
SQLファイル用意(migrateで実行されるSQL)
※Java API実行時と同じく【src/main/resources/db/migration】配下にSQLファイル配置
Mavenコマンド実行
mvn flyway:migrate