[Spring Boot] テスト時と本番稼働時でマイグレーションの挙動を変える方法

# はじめに
SpringBootが自動でマイグレーションをやって欲しい時とそうでない時とがあったので、備忘録かねてスイッチングの方法を残しておきます。

# Configurationします
```kotlin 
@Configuration
@EnableJpaRepositories(
        basePackages = ["<パッケージ名>"],
        entityManagerFactoryRef = "databaseEntityManager",
        transactionManagerRef = "databaseTransactionManager")
class DatabaseSourceConfiguration {
    @Bean
    @Primary
    fun databaseProperties(): DataSourceProperties {
        return DataSourceProperties()
    }

    @Bean
    @Primary
    @Autowired
    fun databaseDataSource(
            @Qualifier("DatabaseProperties") properties: DataSourceProperties
    ): DataSource {
        return properties.initializeDataSourceBuilder().build()
    }

    @Bean
    @Primary
    @Autowired
    fun databaseEntityManager(
            builder: EntityManagerFactoryBuilder,
            @Qualifier("databaseDataSource") dataSource: DataSource
    ): LocalContainerEntityManagerFactoryBean {
        return builder.dataSource(dataSource)
                .packages("<パッケージ名>")
                .properties(mapOf("hibernate.hbm2ddl.auto" to "update"))
                .persistenceUnit("primary")
                .build()
    }

    @Bean
    @Primary
    @Autowired
    fun primaryDBTransactionManager(
            @Qualifier("databaseEntityManager") databaseEntityManager: EntityManagerFactory
    ): JpaTransactionManager {
        return JpaTransactionManager(databaseEntityManager)
    }
}
```

# 要求
` databaseEntityManager`の`.properties(mapOf("hibernate.hbm2ddl.auto" to "update"))`この部分ですが、
アプリ起動時にSpringがEntityを見て自動でマイグレーション(スキーマの更新)をしてくれるpropertyになっています。
自動テストなどでインメモリDBを使う場面だとテスト終了後にDBが丸ごと消えるため、テストを開始する度にマイグレーションしてくれるのはありがたいです。
ところが本番稼働で別のマイグレーションツールを使ったりする場面では、Springのマイグレーションと別ツールのマイグレーションが両方とも走るので気持ち悪いです。
なので、本番稼働時だけ`.properties(mapOf("hibernate.hbm2ddl.auto" to "none"))`としてSpringのマイグレーションを停止させたいなと思いました。

# application.propertiesから`@Value`で取ってくる
本番稼働用とテスト用のapplication.propertiesに以下を書きます。

```properties
# 本番用
boot.mode = production
# テスト用
boot.mode = test
```
これをKotlin側へ持ってきます。

```kotlin
import org.springframework.beans.factory.annotation.Value
class DatabaseSourceConfiguration {
    @Value("\${boot.mode}")
    lateinit var bootMode: String
    ...
    @Bean
    @Primary
    @Autowired
    fun databaseEntityManager(
            builder: EntityManagerFactoryBuilder,
            @Qualifier("databaseDataSource") dataSource: DataSource
    ): LocalContainerEntityManagerFactoryBean {
        return builder.dataSource(dataSource)
                .packages("<パッケージ名>")
                .properties(
                        if (bootMode == "test") {
                            //テスト時は自動でマイグレーションする
                            mapOf("hibernate.hbm2ddl.auto" to "update")
                        } else {
                            mapOf("hibernate.hbm2ddl.auto" to "none")
                        })
                .persistenceUnit("primary")
                .build()
    }
　　 ...
}
```
本記事の要求を満たすだけならこれでもいいのですが、場合によってはプロパティの有無によってスイッチングしたい場合もあるかと思います。
余計なプロパティを書かないに越したことはありません。

# 本番application.propertiesに余計なプロパティを入れたくない場合

```
# テスト用
test.mode = true
```
できることなら本番用のpropertiesを汚したくはないのでテスト用のプロパティだけに上記を書きます。
その場合、本番用には`test.mode`なるプロパティは存在しないので、本番起動時にエラーになります。

```kotlin
    @Value("\${test.mode}")
    lateinit var testMode: String
    // var testMode: String? = null
    // ↑これでもダメ
```
こういう場合は`Environment`を使いましょう。

```kotlin 
import org.springframework.core.env.Environment
class DatabaseSourceConfiguration {
    @Autowired
    lateinit var environment: Environment

    ...
    @Bean
    @Primary
    @Autowired
    fun databaseEntityManager(
            builder: EntityManagerFactoryBuilder,
            @Qualifier("databaseDataSource") dataSource: DataSource
    ): LocalContainerEntityManagerFactoryBean {
        return builder.dataSource(dataSource)
                .packages("<パッケージ名>")
                .properties(
                        if (isTesting()) {
                            //テスト時は自動でマイグレーションする
                            mapOf("hibernate.hbm2ddl.auto" to "update")
                        } else {
                            mapOf("hibernate.hbm2ddl.auto" to "none")
                        })
                .persistenceUnit("primary")
                .build()
    }
　　 ...

　　 private fun isTesting(): Boolean = environment.getProperty("test.mode") == "true"
}
```
仮に`test.mode`なるプロパティが存在しなかった場合には、エラーになることもなく
`environment.getProperty("test.mode")`に`null`がちゃんと入ってくれます。

# 最後に
もっとスマートな方法があればご教示いただけますと幸いです！