1. 概要

Spring Bootを使用すると、データベースの変更を非常に簡単に管理できます。 デフォルト設定のままにすると、パッケージ内のエンティティが検索され、それぞれのテーブルが自動的に作成されます。

ただし、データベースの変更をよりきめ細かく制御する必要がある場合があります。 そして、Springでdata.sqlファイルとschema.sqlファイルを使用できるのはそのときです。

2. data.sqlファイル

また、ここでJPAを使用していると仮定し、プロジェクトで単純なCountryエンティティを定義します。

@Entity
public class Country {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Integer id;
    
    @Column(nullable = false)
    private String name;

    //...
}

アプリケーションを実行すると、 Spring Bootは空のテーブルを作成しますが、何も入力しません。

これを行う簡単な方法は、data.sqlという名前のファイルを作成することです。

INSERT INTO country (name) VALUES ('India');
INSERT INTO country (name) VALUES ('Brazil');
INSERT INTO country (name) VALUES ('USA');
INSERT INTO country (name) VALUES ('Italy');

クラスパスでこのファイルを使用してプロジェクトを実行すると、Springはそれを取得し、データベースへの入力に使用します。

3. schema.sqlファイル

デフォルトのスキーマ作成メカニズムに依存したくない場合があります。

このような場合、カスタムschema.sqlファイルを作成できます。

CREATE TABLE country (
    id   INTEGER      NOT NULL AUTO_INCREMENT,
    name VARCHAR(128) NOT NULL,
    PRIMARY KEY (id)
);

Springはこのファイルを取得し、スキーマの作成に使用します。

スクリプトベースの初期化、すなわち schema.sqldata.sqlを介して、Hibernateの初期化を一緒に行うと、いくつかの問題が発生する可能性があります。

Hibernateの自動スキーマ作成を無効にするか、次のようにします。

spring.jpa.hibernate.ddl-auto=none

これにより、スクリプトベースの初期化がschema.sqlおよびdata.sqlを直接使用して実行されるようになります。

スクリプトベースのスキーマ作成とデータポピュレーションを組み合わせたHibernate自動スキーマ生成の両方を引き続き使用する場合は、次を使用する必要があります。

spring.jpa.defer-datasource-initialization=true

これにより、Hibernateスキーマの作成が実行された後、追加のスキーマ変更のためにさらに schema.sql が読み取られ、data.sqlが実行されてデータベースにデータが入力されます。 

また、スクリプトベースの初期化は、デフォルトでは組み込みデータベースに対してのみ実行されます。常にスクリプトを使用してデータベースを初期化するには、次を使用する必要があります。

spring.sql.init.mode=always

SQLスクリプトを使用したデータベースの初期化に関するSpringの公式ドキュメントを参照してください。

4. Hibernateを使用したデータベース作成の制御

Springは、HibernateがDDL生成に使用するJPA固有のプロパティを提供します: spring.jpa.hibernate.ddl-auto

標準のHibernateプロパティ値は、 create update create-drop validate noneです。

  • create – Hibernateは最初に既存のテーブルを削除し、次に新しいテーブルを作成します。
  • update –マッピング(注釈またはXML)に基づいて作成されたオブジェクトモデルが既存のスキーマと比較され、Hibernateが差分に従ってスキーマを更新します。 アプリケーションで不要になった場合でも、既存のテーブルまたは列が削除されることはありません。
  • create-drop create に似ていますが、すべての操作が完了した後にHibernateがデータベースをドロップする点が追加されています。 通常、単体テストに使用されます
  • validate – Hibernateは、テーブルと列が存在するかどうかのみを検証します。 それ以外の場合は、例外がスローされます。
  • none –この値は、DDL生成を効果的にオフにします。

Spring Bootは、スキーママネージャーが検出されなかった場合、このパラメーター値を内部的に create-drop にデフォルト設定します。それ以外の場合は、noneに設定します。

値を慎重に設定するか、他のメカニズムの1つを使用してデータベースを初期化する必要があります。

5. データベーススキーマ作成のカスタマイズ

デフォルトでは、SpringBootは埋め込まれたDataSourceのスキーマを自動的に作成します。

この動作を制御またはカスタマイズする必要がある場合は、プロパティspring.sql.init.modeを使用できます。 このプロパティは、次の3つの値のいずれかを取ります。

  • 常に–常にデータベースを初期化します
  • embedded –組み込みデータベースが使用されている場合は常に初期化されます。 プロパティ値が指定されていない場合、これがデフォルトです。
  • never –データベースを初期化しない

特に、埋め込まれていないデータベース、たとえばMySQLまたはPostGreSQLを使用していて、そのスキーマを初期化する場合は、このプロパティを常にに設定する必要があります。

このプロパティは、SpringBoot2.5.0で導入されました。 以前のバージョンのSpringBootを使用している場合は、spring.datasource.initialization-modeを使用する必要があります。

6.  @Sql

Springは、 @Sql アノテーションも提供します。これは、テストスキーマを初期化してデータを設定するための宣言型の方法です。

@Sql アノテーションを使用して新しいテーブルを作成し、統合テスト用の初期データをテーブルにロードする方法を見てみましょう。

@Sql({"/employees_schema.sql", "/import_employees.sql"})
public class SpringBootInitialLoadIntegrationTest {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Test
    public void testLoadDataForTestClass() {
        assertEquals(3, employeeRepository.findAll().size());
    }
}

@Sqlアノテーションの属性は次のとおりです。

  • config –SQLスクリプトのローカル構成。 これについては、次のセクションで詳しく説明します。
  • executePhase BEFORE_TEST_METHODまたはAFTER_TEST_METHODのいずれかで、スクリプトをいつ実行するかを指定することもできます。
  • ステートメント–実行するインラインSQLステートメントを宣言できます。
  • scripts –実行するSQLスクリプトファイルへのパスを宣言できます。 これは、属性のエイリアスです。

@Sql アノテーションは、クラスレベルまたはメソッドレベルで使用できます。

特定のテストケースに必要な追加データを、そのメソッドに注釈を付けてロードします。

@Test
@Sql({"/import_senior_employees.sql"})
public void testLoadDataForTestCase() {
    assertEquals(5, employeeRepository.findAll().size());
}

7.  @SqlConfig  

@SqlConfig アノテーションを使用して、SQLスクリプトを解析および実行する方法を構成できます。

@SqlConfig はクラスレベルで宣言でき、グローバル構成として機能します。 または、これを使用して特定の@Sqlアノテーションを構成することもできます。

SQLスクリプトのエンコーディングと、スクリプトを実行するためのトランザクションモードを指定する例を見てみましょう。

@Test
@Sql(scripts = {"/import_senior_employees.sql"}, 
  config = @SqlConfig(encoding = "utf-8", transactionMode = TransactionMode.ISOLATED))
public void testLoadDataForTestCase() {
    assertEquals(5, employeeRepository.findAll().size());
}

そして、@SqlConfigのさまざまな属性を見てみましょう。

  • blockCommentStartDelimiter –SQLスクリプトファイルのブロックコメントの開始を識別する区切り文字
  • blockCommentEndDelimiter –SQLスクリプトファイルのブロックコメントの終わりを示す区切り文字
  • commentPrefix –SQLスクリプトファイル内の単一行コメントを識別するためのプレフィックス
  • dataSource –スクリプトとステートメントが実行されるjavax.sql.DataSourceBeanの名前
  • encoding –SQLスクリプトファイルのエンコーディング。 デフォルトはプラットフォームエンコーディングです
  • errorMode –スクリプトの実行中にエラーが発生したときに使用されるモード
  • Separator –個々のステートメントを区切るために使用される文字列。 デフォルトは「–」です
  • transactionManager –トランザクションに使用されるPlatformTransactionManagerのBean名
  • transactionMode –トランザクションでスクリプトを実行するときに使用されるモード

8.  @SqlGroup  

Java 8以降では、繰り返し注釈を使用できます。 この機能は、@Sqlアノテーションにも利用できます。 Java 7以下の場合、コンテナアノテーションがあります— @SqlGroup

@SqlGroupアノテーションを使用して、複数の@Sqlアノテーションを宣言します

@SqlGroup({
  @Sql(scripts = "/employees_schema.sql", 
    config = @SqlConfig(transactionMode = TransactionMode.ISOLATED)),
  @Sql("/import_employees.sql")})
public class SpringBootSqlGroupAnnotationIntegrationTest {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Test
    public void testLoadDataForTestCase() {
        assertEquals(3, employeeRepository.findAll().size());
    }
}

9. 結論

この簡単な記事では、schema.sqlファイルとdata.sqlファイルを利用して、初期スキーマを設定し、データを入力する方法を説明しました。

また、 @Sql @SqlConfig @SqlGroup アノテーションを使用して、テスト用のテストデータをロードする方法についても説明しました。

このアプローチは基本的で単純なシナリオに適していることを覚えておいてください。高度なデータベース処理には、LiquibaseFlywayなどのより高度で洗練されたツールが必要です。

コードスニペットは、いつものように、GitHubにあります。