(第7回) schema.sqlを使用してDBのテーブル作成【H2, Spring Boot2】

H2入門の第7回です。DDLのschema.sqlファイルを作成し、Spring Bootアプリ起動時にDBのテーブル生成をするための設定を説明しています。Spring Bootの初心者・入門者の方は、参考にしてみてください。

環境と今回の目的

最終更新日:2021/4/19

以前にSpring Bootアプリの起動時に、Spring Data JPAの機能でエンティティクラスからDBのテーブルを自動生成し、その動作結果をH2 Consoleで確認しました。

(第2回) H2の接続設定、エンティティクラスの作成、data.sqlで初期データ作成【Spring Boot2】
(第3回) H2のブラウザ管理ツール"H2 Console"の使い方【H2, Spring Boot2】

今度は、エンティティクラスからテーブル生成をするのではなく、schema.sqlファイルにDDL(Data Definition Language)を自分で書いて準備してテーブル生成をしてみたいと思います。

・開発環境やバージョンは以下の通りです。
OS:macOS Catalina(バージョン10.15.5)
開発環境:Eclipse(Pleiades All in One、4.16(2020-06)、Java Full Edition版)、(*2020/8/28時点の最新安定版です)
Spring Boot:バージョン2.3.3
Java:11
データベース:H2

DDLのschema.sqlを作成しテーブル生成する

まず、プロジェクトのsrc/main/resourcesディレクトリ下にschema.sqlファイルを作成して編集します。
(*schema.sqlに書くsqlをコメントアウトしたい場合、行頭に"--"を付けます)

create table diary (id integer generated by default as identity, comment varchar(255), create_datetime timestamp not null, primary key (id));
schema.sqlの内容は、diaryテーブルを作成するだけのDDLです。エンティティクラスDiaryから生成されるテーブルと同じテーブル構成です。

Spring Bootでは、src/main/resourcesディレクトリ下にschema.sql(DDL)とdata.sql(DML)という名前のファイルがあると、そのファイル内に書いてあるsqlをアプリ起動時に自動で実行してくれます。

とりあえずこれだけで、Spring Bootアプリを起動してみます。
起動時のコンソールビューのログを一部表示します。

〜
2020-10-18 17:35:42.112  INFO 10235 --- [  restartedMain] jdbc.sqlonly                             : create table diary (id integer generated by default as identity, comment varchar(255), create_datetime 
timestamp not null, primary key (id)) 

2020-10-18 17:35:42.127  INFO 10235 --- [  restartedMain] jdbc.sqlonly                             : INSERT INTO diary(comment, create_datetime) VALUES('今日は晴れ。コメント1', LOCALTIME()) 

2020-10-18 17:35:42.135  INFO 10235 --- [  restartedMain] jdbc.sqlonly                             : INSERT INTO diary(comment, create_datetime) VALUES('comment 2', LOCALTIME()) 

2020-10-18 17:35:42.135  INFO 10235 --- [  restartedMain] jdbc.sqlonly                             : INSERT INTO diary(comment, create_datetime) VALUES('こめんと3', LOCALTIME()) 
〜
2020-10-18 17:35:43.409  INFO 10235 --- [  restartedMain] jdbc.sqlonly                             : select diary0_.id as id1_0_, diary0_.comment as comment2_0_, diary0_.create_datetime as create_d3_0_ 
from diary diary0_ 

2020-10-18 17:35:43.411  INFO 10235 --- [  restartedMain] jdbc.resultsettable                      : 
|---|--------|----------------|
|id |comment |create_datetime |
|---|--------|----------------|
|---|--------|----------------|
〜

SQLログの出力は、Log4jdbc-log4j2を使用してやっています。Log4jdbc-log4j2でのSQLロギングの設定は別ページで説明していますので、そちらを参考にしてください。
H2の管理ツール"H2 Console"、Log4jdbc-log4j2用のドライバDriverSpy使用時【Spring Boot2】

SQLログを確認すると、今回のschema.sqlで書いたcreate table diaryが実行され、その後にdata.sqlで書いた初期データ作成用のINSERT INTO diaryが実行されています。

これで、diaryテーブルが生成され3レコード分のデータが作成されたはずなんですが、その後のdiaryテーブルから全件取得のselect SQLの結果が空になっています。この結果はおかしいです。

H2 Console(アクセスURLは「http://localhost:8080/h2-console 」)でも確認してみましたが、diaryテーブルは生成されていますが、データは作成されておらず空っぽでした。

ちなみにschema.sqlでdiaryテーブルのDDLを作成しない場合、エンティティクラスDiaryからもdiaryテーブルは作成されませんでした。
テーブル生成をschema.sqlでやるか、エンティティクラスでやるかを選択する事はできますが、両方を行うことはできないという事です。それはそうですね。両方が機能したら同じテーブルを生成することになるのでその時点でエラーになりますから。

spring.jpa.hibernate.ddl-autoの設定

Spring Bootのデータベースの初期化に関してですが、application.propertiesファイルのspring.jpa.hibernate.ddl-autoプロパティによって制御できます。

今回は、application.propertiesファイルに1行コードを追加します。

spring.jpa.hibernate.ddl-auto=none
"none"を設定する事で、アプリ起動時にSpring Data JPAによって内部実装のHibernateがテーブル作成機能を無効にできます。つまり、エンティティクラスからテーブル生成しなくなるので、schema.sqlとdata.sqlによってテーブル生成とデータ生成ができるようになります。

これで再度Spring Bootアプリを起動して確認してみます。

〜
2020-10-18 17:35:42.112  INFO 10235 --- [  restartedMain] jdbc.sqlonly                             : create table diary (id integer generated by default as identity, comment varchar(255), create_datetime 
timestamp not null, primary key (id)) 

2020-10-18 17:35:42.127  INFO 10235 --- [  restartedMain] jdbc.sqlonly                             : INSERT INTO diary(comment, create_datetime) VALUES('今日は晴れ。コメント1', LOCALTIME()) 

2020-10-18 17:35:42.135  INFO 10235 --- [  restartedMain] jdbc.sqlonly                             : INSERT INTO diary(comment, create_datetime) VALUES('comment 2', LOCALTIME()) 

2020-10-18 17:35:42.135  INFO 10235 --- [  restartedMain] jdbc.sqlonly                             : INSERT INTO diary(comment, create_datetime) VALUES('こめんと3', LOCALTIME()) 
〜
2020-10-18 19:30:22.615  INFO 12151 --- [  restartedMain] jdbc.sqlonly                             : select diary0_.id as id1_0_, diary0_.comment as comment2_0_, diary0_.create_datetime as create_d3_0_ 
from diary diary0_ 

2020-10-18 19:30:22.624  INFO 12151 --- [  restartedMain] jdbc.resultsettable                      : 
|---|------------|----------------------|
|id |comment     |create_datetime       |
|---|------------|----------------------|
|1  |今日は晴れ。コメント1 |2020-10-18 19:30:22.0 |
|2  |comment 2   |2020-10-18 19:30:22.0 |
|3  |こめんと3       |2020-10-18 19:30:22.0 |
〜
今度はdiaryテーブルから全件取得のselect SQLの結果も出力されました。期待していた結果です。

ちなみに、spring.jpa.hibernate.ddl-autoに設定できる値は、none,validate,update,create,create-dropです。
2.1と少し古いバージョンのSpring Bootのデータベースの初期化マニュアルを参考にしてください。
84. データベースの初期化

ただ、プロジェクトの方針にもよると思いますが、もしschema.sqlからテーブル生成をする事を想定しているなら"none"にしておいた方がいいと思います。

最後に、次回

今回は、Spring BootアプリのDBのテーブル生成をschema.sqlからやる設定をしました。テーブル生成をエンティティクラスで行うか、schema.sqlで行うかの選択は、個人やプロジェクトによると思います。

また、DDLをschema.sqlなどのファイルで管理する場合ですが、FlywayというオープンソースのDBマイグレーションツールがありますので、それを利用するのも便利です。
アプリで使うDBのテーブル構成はどんどん変化していきますから、Flywayを使うとDDLファイルの簡単なバージョン管理が行えます。
いつになるか分かりませんが、近いうちにFlywayを試しながら、使い方の説明などを書こうと思います。

次回ですが、今まではH2データベースはインメモリで使ってきましたが、データの保存先をファイルにしてみたいと思います。保存先をファイルにする事でDBのデータが永続化されるので、Spring Bootアプリを何度起動し直してもデータは残ります。

(第8回) H2のデータ保存先をファイルに設定、データの永続化【Spring Boot2】
H2データベース入門【Spring Boot】トップに戻る