データベースの初期化

SQL データベースは、スタックの種類に応じてさまざまな方法で初期化できます。もちろん、データベースが別のプロセスであれば、手動で行うこともできます。スキーマ生成には単一のメカニズムを使用することをお勧めします。

Hibernate を使用したデータベースの初期化

spring.jpa.hibernate.ddl-auto を設定すると、Hibernate のデータベース初期化を制御できます。サポートされる値は nonevalidateupdatecreatecreate-drop です。Spring Boot は、組み込みデータベースを使用しているかどうかに基づいてデフォルト値を選択します。組み込みデータベースは、Connection 型と JDBC URL を見て識別されます。hsqldbh2、または derby は組み込みデータベースで、その他は組み込みデータベースではありません。組み込みデータベースが識別され、スキーママネージャー (Flyway または Liquibase) が検出されなかった場合、ddl-auto はデフォルトで create-drop になります。それ以外の場合は、デフォルトで none になります。

インメモリから「実際の」データベースに切り替えるときは、新しいプラットフォームにテーブルとデータが存在することを前提としないように注意してください。ddl-auto を明示的に設定するか、他のメカニズムのいずれかを使用してデータベースを初期化する必要があります。

org.hibernate.SQL ロガーを有効にすることで、スキーマの作成を出力できます。デバッグモードを有効にすると、これが自動的に行われます。

さらに、Hibernate が最初からスキーマを作成する場合(つまり、ddl-auto プロパティが create または create-drop に設定される場合)、クラスパスのルートにある import.sql という名前のファイルが起動時に実行されます。これは、デモや、慎重にテストする場合に役立ちますが、おそらく本番環境のクラスパスに配置したいものではありません。これは Hibernate 機能です(Spring とは関係ありません)。

基本的な SQL スクリプトを使用してデータベースを初期化する

Spring Boot は、JDBC DataSource または R2DBC ConnectionFactory のスキーマ (DDL スクリプト) を自動的に作成し、そのデータ (DML スクリプト) を初期化できます。

デフォルトでは、スキーマスクリプトは optional:classpath*:schema.sql からロードされ、データスクリプトは optional:classpath*:data.sql からロードされます。これらのスキーマスクリプトとデータスクリプトの場所は、それぞれ spring.sql.init.schema-locations と spring.sql.init.data-locations を使用してカスタマイズできます。optional: プレフィックスは、ファイルが存在しない場合でもアプリケーションが開始されることを意味します。ファイルが存在しないときにアプリケーションを起動できないようにするには、optional: プレフィックスを削除します。

さらに、Spring Boot は optional:classpath*:schema-${platform}.sql および optional:classpath*:data-${platform}.sql ファイル (存在する場合) を処理します。ここで、${platform} は spring.sql.init.platform の値です。これにより、必要に応じてデータベース固有のスクリプトに切り替えることができます。例: データベースのベンダー名 (hsqldbh2oraclemysqlpostgresql など) に設定することを選択できます。

デフォルトでは、SQL データベースの初期化は、組み込みのメモリ内データベースを使用する場合にのみ実行されます。SQL データベースをその型に関係なく常に初期化するには、spring.sql.init.mode を always に設定します。同様に、初期化を無効にするには、spring.sql.init.mode を never に設定します。デフォルトでは、Spring Boot はスクリプトベースのデータベース初期化機能のフェイルファスト機能を有効にします。つまり、スクリプトによって例外が発生すると、アプリケーションは起動に失敗します。spring.sql.init.continue-on-error を設定することで、その動作を調整できます。

スクリプトベースの DataSource 初期化は、デフォルトで、JPA EntityManagerFactory Bean が作成される前に実行されます。schema.sql を使用して JPA 管理エンティティのスキーマを作成し、data.sql を使用してスキーマを設定できます。複数のデータソース初期化テクノロジを使用することはお勧めしませんが、スクリプトベースの DataSource 初期化を Hibernate によって実行されるスキーマ作成に基づいて構築できるようにする場合は、spring.jpa.defer-datasource-initialization を true に設定します。これにより、EntityManagerFactory Bean が作成および初期化されるまで、データソースの初期化が延期されます。次に、schema.sql を使用して、Hibernate によって実行されるスキーマ作成に追加を行うことができ、data.sql を使用してそれを設定できます。

初期化スクリプトは、単一行コメントの -- とブロックコメントの /* */ をサポートします。他のコメント形式はサポートされていません。

Flyway や Liquibase などの高レベルのデータベース移行ツールを使用している場合は、単独で使用してスキーマを作成および初期化する必要があります。基本的な schema.sql および data.sql スクリプトを Flyway または Liquibase と一緒に使用することはお勧めできません。サポートは将来のリリースで削除される予定です。

上位レベルのデータベース移行ツールを使用してテストデータを初期化する必要がある場合は、Flyway および Liquibase に関するセクションを参照してください。

Spring Batch データベースを初期化する

Spring Batch を使用する場合、ほとんどの一般的なデータベースプラットフォーム用の SQL 初期化スクリプトがあらかじめパッケージ化されています。Spring Boot は、データベース型を検出し、起動時にこれらのスクリプトを実行できます。組み込みデータベースを使用する場合、これはデフォルトで行われます。次の例に示すように、任意のデータベース型に対して有効にすることもできます。

  • プロパティ

  • YAML

spring.batch.jdbc.initialize-schema=always
spring:
  batch:
    jdbc:
      initialize-schema: "always"

spring.batch.jdbc.initialize-schema を never に設定することにより、初期化を明示的にオフにすることもできます。

高レベルのデータベース移行ツールを使用する

Spring Boot は、Flyway (英語) Liquibase (英語) の 2 つの高レベルの移行ツールをサポートしています。

起動時に Flyway データベース移行を実行する

起動時に Flyway データベースの移行を自動的に実行するには、org.flywaydb:flyway-core をクラスパスに追加します。

通常、移行は V<VERSION>__<NAME>.sql という形式のスクリプトです(<VERSION> は、"1" や "2_1" などのアンダースコアで区切られたバージョンです)。デフォルトでは、これらは classpath:db/migration と呼ばれるディレクトリにありますが、spring.flyway.locations を設定することでその場所を変更できます。これは、1 つ以上の classpath: または filesystem: ロケーションのコンマ区切りリストです。例: 次の構成では、デフォルトのクラスパスの場所と /opt/migration ディレクトリの両方でスクリプトを検索します。

  • プロパティ

  • YAML

spring.flyway.locations=classpath:db/migration,filesystem:/opt/migration
spring:
  flyway:
    locations: "classpath:db/migration,filesystem:/opt/migration"

特別な {vendor} プレースホルダーを追加して、ベンダー固有のスクリプトを使用することもできます。以下を想定します。

  • プロパティ

  • YAML

spring.flyway.locations=classpath:db/migration/{vendor}
spring:
  flyway:
    locations: "classpath:db/migration/{vendor}"

db/migration を使用するのではなく、前述の構成では、データベースの型(MySQL の db/migration/mysql など)に従って使用するディレクトリを設定します。サポートされているデータベースのリストは、DatabaseDriver (Javadoc) で入手できます。

移行は Java で作成することもできます。Flyway は、JavaMigration を実装する Bean で自動構成されます。

FlywayProperties (Javadoc) は、Flyway のほとんどの設定と、移行を無効にするか、場所の確認をオフにするために使用できる追加プロパティの小さなセットを提供します。構成をさらに制御する必要がある場合は、FlywayConfigurationCustomizer Bean の登録を検討してください。

Spring Boot は Flyway.migrate() を呼び出して、データベースの移行を実行します。さらに制御したい場合は、FlywayMigrationStrategy (Javadoc) を実装する @Bean を提供してください。

Flyway は SQL および Java コールバック (英語) をサポートします。SQL ベースのコールバックを使用するには、コールバックスクリプトを classpath:db/migration ディレクトリに配置します。Java ベースのコールバックを使用するには、Callback を実装する 1 つ以上の Bean を作成します。このような Bean は Flyway に自動的に登録されます。@Order を使用するか、Ordered を実装することでオーダーできます。非推奨の FlywayCallback インターフェースを実装する Bean も検出できますが、Callback Bean と一緒に使用することはできません。

デフォルトでは、Flyway はコンテキストで(@Primary) DataSource をオートワイヤーし、それを移行に使用します。別の DataSource を使用する場合は、作成して、その @Bean を @FlywayDataSource としてマークできます。そのようにして 2 つのデータソースが必要な場合は、別のデータソースを作成して @Primary としてマークしてください。または、外部プロパティで spring.flyway.[url,user,password] を設定することにより、Flyway のネイティブ DataSource を使用できます。spring.flyway.url または spring.flyway.user のいずれかを設定するだけで、Flyway が独自の DataSource を使用できます。3 つのプロパティのいずれかが設定されていない場合、同等の spring.datasource プロパティの値が使用されます。

Flyway を使用して、特定のシナリオのデータを提供することもできます。例: テスト固有の移行を src/test/resources に配置できます。これらの移行は、テストのためにアプリケーションが開始されたときにのみ実行されます。また、特定のプロファイルがアクティブな場合にのみ特定の移行が実行されるように、プロファイル固有の構成を使用して spring.flyway.locations をカスタマイズできます。例: application-dev.properties では、次の設定を指定できます。

  • プロパティ

  • YAML

spring.flyway.locations=classpath:/db/migration,classpath:/dev/db/migration
spring:
  flyway:
    locations: "classpath:/db/migration,classpath:/dev/db/migration"

その設定では、dev/db/migration の移行は、dev プロファイルがアクティブな場合にのみ実行されます。

起動時に Liquibase データベースの移行を実行する

起動時に Liquibase データベースの移行を自動的に実行するには、クラスパスに org.liquibase:liquibase-core を追加します。

org.liquibase:liquibase-core をクラスパスに追加すると、データベースの移行は、アプリケーションの起動時とテストの実行前の両方でデフォルトで実行されます。この動作は、spring.liquibase.enabled プロパティを使用してカスタマイズし、main 構成と test 構成で異なる値を設定できます。データベースを初期化するために 2 つの異なる方法を使用することはできません(たとえば、アプリケーションの起動には Liquibase、テストの実行には JPA)。

デフォルトでは、マスター変更ログは db/changelog/db.changelog-master.yaml から読み取られますが、spring.liquibase.change-log を設定して場所を変更できます。YAML に加えて、Liquibase は JSON、XML、SQL 変更ログ形式もサポートしています。

デフォルトでは、Liquibase は(@Primary) DataSource をコンテキストでオートワイヤーし、それを移行に使用します。別の DataSource を使用する必要がある場合は、それを作成して、その @Bean を @LiquibaseDataSource としてマークできます。その場合、2 つのデータソースが必要な場合は、別のデータソースを作成して、@Primary としてマークすることを忘れないでください。または、外部プロパティで spring.liquibase.[driver-class-name,url,user,password] を設定することにより、Liquibase のネイティブ DataSource を使用できます。Liquibase が独自の DataSource を使用するには、spring.liquibase.url または spring.liquibase.user を設定するだけで十分です。3 つのプロパティのいずれかが設定されていない場合、対応する spring.datasource プロパティの値が使用されます。

コンテキスト、デフォルトスキーマなどの利用可能な設定の詳細については、LiquibaseProperties (Javadoc) を参照してください。

テストのみの移行には Flyway を使用する

テストデータベースにデータを取り込む Flyway 移行を作成する場合は、src/test/resources/db/migration に配置します。たとえば、src/test/resources/db/migration/V9999__test-data.sql という名前のファイルは、本番環境の移行後、テストを実行している場合にのみ実行されます。このファイルを使用して、必要なテストデータを作成できます。このファイルは、uber jar またはコンテナーにはパッケージ化されません。

テストのみの移行には Liquibase を使用する

テストデータベースにデータを取り込む Liquibase 移行を作成する場合は、運用変更ログも含まれるテスト変更ログを作成する必要があります。

まず、テストの実行時に別の変更ログを使用するように Liquibase を構成する必要があります。これを行う 1 つの方法は、Spring Boot test プロファイルを作成し、そこに Liquibase プロパティを配置することです。そのためには、src/test/resources/application-test.properties という名前のファイルを作成し、そこに次のプロパティを追加します。

  • プロパティ

  • YAML

spring.liquibase.change-log=classpath:/db/changelog/db.changelog-test.yaml
spring:
  liquibase:
    change-log: "classpath:/db/changelog/db.changelog-test.yaml"

これにより、test プロファイルで実行するときに異なる変更ログを使用するように Liquibase が構成されます。

ここで、src/test/resources/db/changelog/db.changelog-test.yaml に変更ログファイルを作成します。

databaseChangeLog:
  - include:
      file: classpath:/db/changelog/db.changelog-master.yaml
  - changeSet:
      runOrder: "last"
      id: "test"
      changes:
        # Insert your changes here

この変更ログはテストの実行時に使用され、uber jar またはコンテナーにはパッケージ化されません。これには運用変更ログが含まれており、新しい変更セットを宣言します。runOrder: last 設定では、すべての運用変更セットが実行された後に実行されるように指定されています。たとえば、挿入チェンジセット (英語) を使用してデータを挿入したり、SQL チェンジセット (英語) を使用して SQL を直接実行したりできるようになりました。

最後に、テストの実行時に test プロファイルをアクティブにするように Spring Boot を構成します。これを行うには、@ActiveProfiles("test") アノテーションを @SpringBootTest アノテーション付きテストクラスに追加します。

初期化されたデータベースに依存する

データベースの初期化は、アプリケーションがアプリケーションコンテキストのリフレッシュの一部として起動しているときに実行されます。起動時に初期化されたデータベースにアクセスできるようにするために、データベース初期化子として機能する Bean と、データベースの初期化が必要な Bean が自動的に検出されます。初期化が初期化されたデータベースに依存する Bean は、データベースを初期化する Bean に依存するように構成されます。起動時に、アプリケーションがデータベースにアクセスしようとして初期化されていない場合は、データベースを初期化し、データベースの初期化を要求する Bean の追加検出を構成できます。

データベースイニシャライザーを検出する

Spring Boot は、SQL データベースを初期化する次の型の Bean を自動的に検出します。

  • DataSourceScriptDatabaseInitializer

  • EntityManagerFactory

  • Flyway

  • FlywayMigrationInitializer

  • R2dbcScriptDatabaseInitializer

  • SpringLiquibase

データベース初期化ライブラリにサードパーティのスターターを使用している場合は、他の型の Bean も自動的に検出されるようにディテクターを提供する場合があります。他の Bean を検出するには、DatabaseInitializerDetector の実装を META-INF/spring.factories に登録します。

データベースの初期化に依存する Bean を検出する

Spring Boot は、データベースの初期化に依存する次の型の Bean を自動的に検出します。

  • AbstractEntityManagerFactoryBean (spring.jpa.defer-datasource-initialization が true に設定されていない限り)

  • DSLContext (jOOQ)

  • EntityManagerFactory (spring.jpa.defer-datasource-initialization が true に設定されていない限り)

  • JdbcClient

  • JdbcOperations

  • NamedParameterJdbcOperations

サードパーティのスターターデータアクセスライブラリを使用している場合は、他の型の Bean も自動的に検出されるようにディテクターを提供する場合があります。他の Bean を検出するには、DependsOnDatabaseInitializationDetector の実装を META-INF/spring.factories に登録します。または、Bean のクラスまたはその @Bean メソッドに @DependsOnDatabaseInitialization アノテーションを付けます。