(第8回)H2のデータをファイルに保存、データの永続化【Spring Boot2】

H2入門の第8回です。H2データベースのデータ保存をファイルにする設定について説明します。H2をファイルベースで利用する事でデータを永続化できます。Spring Bootの初心者・入門者の方、独学で(個人で)勉強している方は参考にしてみてください。

環境と今回の目的

最終更新日:2022/11/27

前回のページでは、Spring Bootアプリの起動時にDDLのschema.sqlファイルに書いたSQLで、DBのテーブル生成をする方法について説明しました。
(第7回) schema.sqlを使用してDBのテーブル作成【H2, Spring Boot2】

H2データベースはインストール作業など必要なくSpring Bootアプリで使えるDBで、その手軽さがメリットです。
また、H2のデータ保存先は、インメモリとファイルのどちらでもできます。

これまではH2をメモリベースで使ってきました。インメモリはデータ保存先がメモリなので、起動し直すたびにデータが初期化されます。初期化されても特に問題ない開発途中のアプリや学習用のWebアプリなら、H2をインメモリベースで使うのも手軽で良いでしょう。

ただデータを永続化(起動し直しても以前のデータが使える)して使いたいケースもあります。というか、普通はDBと連携したWebアプリを開発する場合、データは永続化して保存しておきたいケースが多いと思います。

H2でデータを永続化したい場合は、H2をファイルベースで扱えばできます。という事で今回は、H2をファイルベースで使う方法について説明します。

・開発環境やバージョンは以下の通りです。
OS:macOS Big Sur(バージョン11.7.1)
開発環境:Eclipse(Pleiades All in One、4.16(2020-06)、Java Full Edition版)
Spring Boot:バージョン2.7.6
Java:11
データベース:H2

schema.sqlファイル(DDL)の編集

まず、DBのテーブルを作成する(DDLの)schema.sqlファイルを編集します。
■変更前

create table diary (id integer generated by default as identity, comment varchar(255), create_datetime timestamp not null, primary key (id));
■変更後
create table if not exists diary (id integer generated by default as identity, comment varchar(255), create_datetime timestamp not null, primary key (id));

create table SQLに"if not exists diary"を付けるようにしました。これによってDBにdiaryテーブルが存在しない時だけ、crate tabel SQLが実行されてdiaryテーブルが作成されます。

これは、H2 DBをファイルベースで永続化する場合、diaryテーブルの作成は最初の1度で十分だからです。Spring Bootアプリの初回起動時だけ、schema.sqlファイルのcrate table SQLが実行されてdiaryテーブルを作成されます。

DBをインメモリで使う場合、アプリを起動するたびにテーブル定義もデータも初期化されるので、if not existsはあってもなくてもどちらでも構いません。

H2をファイルベースで使うためのapplication.propertiesファイルの設定。spring.sql.init.modeプロパティ

次に、application.propertiesファイルを編集します。H2データベースのデータ保存先をメモリからファイルに変更するので、"spring.datasource.url"プロパティを変更します。
■変更前(インメモリ)

spring.datasource.url=jdbc:log4jdbc:h2:mem:hogedb
■変更後(ファイル)
spring.datasource.url=jdbc:log4jdbc:h2:file:./hogefiledb
変更点は、"file:./hogefiledb"の部分です。

DBファイルの保存先のパスは”./hogefiledb”にしています。この場合、Spring Bootアプリのロケーションディレクトリ直下に"hogefiledb.mv.db"というファイルを作成し、DBファイルを保存します。
(*Spring Bootアプリのロケーションディレクトリは、EclipseでSpring Bootプロジェクトを右クリック->プロパティで確認できます)
また、DBファイル(hogefiledb.mv.db)ができれば、Eclipseを右クリック->リフレッシュを選択して見れるようになるはずです。

ちなみにこの段階でアプリを実行すると、テーブルが存在しないというエラーが発生します。

〜
org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "DIARY" not found (this database is empty); SQL statement:
select diary0_.id as id1_0_, diary0_.comment as comment2_0_, diary0_.create_datetime as create_d3_0_ from diary diary0_ [42104-214]
〜
2022-11-27 12:12:06.392  WARN 48574 --- [  restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 42104, SQLState: 42S04
2022-11-27 12:12:06.392 ERROR 48574 --- [  restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper   : Table "DIARY" not found (this database is empty); SQL statement:
select diary0_.id as id1_0_, diary0_.comment as comment2_0_, diary0_.create_datetime as create_d3_0_ from diary diary0_ [42104-214]
2022-11-27 12:12:06.405  INFO 48574 --- [  restartedMain] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-11-27 12:12:06.438 ERROR 48574 --- [  restartedMain] o.s.boot.SpringApplication               : Application run failed
〜

あとここでは、application.propertiesに"spring.sql.init.mode"プロパティの設定を追加します。

spring.sql.init.mode=always

"spring.sql.init.mode"プロパティは、SQLデータベース初期化を実行するかどうかのモード設定で、alwaysに設定する事でschema.sqlとdata.sqlファイルで設定します。

"spring.sql.init.mode"プロパティの詳細については、別サイトを参考にしてください。
参考サイトから、"spring.sql.init.mode"プロパティの説明を一部抜粋します。

デフォルトでは、SQL データベースの初期化は、組み込みインメモリデータベースを使用している場合にのみ実行されます。タイプに関係なく、SQL データベースを常に初期化するには、spring.sql.init.mode を always に設定します。
(引用元:9.3. 基本的な SQL スクリプトを使用してデータベースを初期化する - Spring Boot 「使い方」ガイド - リファレンスドキュメント

説明を読んで私の理解では、H2をメモリベースではなくファイルベースで使う場合、schema.sqlとdata.sqlによるDB初期化をするには"spring.sql.init.mode=always"を設定する必要があるみたいです。

その他の"spring.sql.init.mode"プロパティの参考サイト。
Spring Boot 2.5 Release Notes
Spring Boot アプリケーションプロパティ設定一覧 - リファレンス

これでapplication.propertiesの設定も完了です。最終的なapplication.propertiesファイルを示します。
(*行頭に#がある行はコメントアウトなので無効な設定ですので、その行は削除しても構いません。)

#DB接続、ファイルに保存する
spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.url=jdbc:log4jdbc:h2:file:./hogefiledb
#spring.datasource.url=jdbc:log4jdbc:h2:mem:hogedb

#Spring Bootのバージョン2.5以上で、DDL(テーブル生成)をschema.sqlファイルではなく、エンティティクラスから作るなら必要。
#spring.jpa.defer-datasource-initialization=true

#Spring Data JPA(Hibernate)のSQLログを出力する設定
#spring.jpa.show-sql=true
#Spring Data JPA(Hibernate)のSQLログのプレースホルダの値を表示
#logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace
#Spring Data JPA(Hibernate)のSQLログ出力の整形
#spring.jpa.properties.hibernate.format_sql=true

logging.level.jdbc.sqlonly=info
#logging.level.jdbc.resultsettable=off
logging.level.jdbc.resultsettable=info
logging.level.jdbc.connection=off
logging.level.jdbc.sqltiming=off
logging.level.jdbc.audit=off
logging.level.jdbc.resultset=off

#Spring Data JPAの機能でエンティティクラスから自動でテーブル生成しないようにする
spring.jpa.hibernate.ddl-auto=none

#DDLとDMLのファイルパスを明示。(クラスパス下のdata.sql、schema.sqlファイルならSpring Bootが自動でロードしてくれる)
spring.sql.init.schema-locations=classpath:schema.sql
spring.sql.init.data-locations=classpath:data.sql

spring.sql.init.mode=always

Spring Boot 本をAmazonで探す [広告]

アプリを起動して動作確認する。SQLとH2 Consoleで確認。

設定は完了したので、Spring Bootアプリを起動して動作確認をします。

問題なく起動できれば、H2保存先をファイルに変更してもメモリベースの時と同じで、EclipseのコンソールにSQLが出力されるはずです。

起動した後にEclipseでSpring Bootプロジェクトを右クリック->リフレッシュを選択すれば、"hogefiledb.mv.db"ファイルができているはずです。これがH2データベースの保存ファイルです。

そして、DB保存先をファイルにしてデータを永続化したので、何度もアプリを起動すれば、SQLログで全件取得のselect SQLの出力結果のレコード数が増えてる事も確認できるはずです。

DBのデータベースを初期化したい場合、"hogefiledb.mv.db"ファイルを削除すればできます。

また、H2の保存先をファイルにしても、H2の管理ツールの「H2 Console」でDBデータを確認する事ができます。
URLは「http://localhost:8080/h2-console 」です。
H2 Consoleログイン、H2保存先をファイル

ログイン画面でログインするための各入力欄です。

ドライバクラス net.sf.log4jdbc.sql.jdbcapi.DriverSpy
JDBC URL jdbc:log4jdbc:h2:file:./hogefiledb
ユーザ名
パスワード

application.propertiesの変更に合わせて、"JDBC URL:"の入力項目だけ変更しました。

"JDBC URL:"に、前回までのメモリベースURLを入力してもログインする事ができるみたいですが、接続先がデータがあるH2 DBファイルではないのでDBにDIARYテーブルは無いです。

Spring Bootアプリ起動時に「未サポートのバージョンか、不正なファイルヘッダを持つデータベースファイルです」というエラーが発生する場合

Spring Bootを他のバージョンに変更したりして試していたら、アプリ起動時にエラーが発生する事がありました。

〜
org.h2.jdbc.JdbcSQLNonTransientConnectionException: ファイル "/Applications/Eclipse_2020-06.app/Contents/workspace/test-h2/hogefiledb.mv.db" は、未サポートのバージョンか、不正なファイルヘッダを持つデータベースファイルです
Unsupported database file version or invalid file header in file "/Applications/Eclipse_2020-06.app/Contents/workspace/test-h2/hogefiledb.mv.db" [90048-214]
〜

エラーメッセージを読む限り、H2データベースの保存ファイル内のバージョンが単純に合わないからだと思います。
エラー解決策として、削除しても問題ないDBファイルなら削除すれば解決します。
ここでは、EclipseのSpring Bootプロジェクトを右クリック->リフレッシュすれば、DBファイルの"hogefiledb.mv.db"と"hogefiledb.trace.db"というファイルがあるので、この2つを削除してアプリを起動し直したら正常に起動しました。

最後に、今後について

今回は、H2のデータを永続化するために、ファイルベースで使うための設定について説明しました。

今後は、H2だけでなくMySQLなどの別のDBをSpring Bootで使用してみたり、Spring Data JPAについてや、Spring BootのウェブアプリやREST API(Web API)のサンプルアプリの作成などについて書いていこうと考えています。

H2データベース入門【Spring Boot】トップに戻る

Kindle Unlimitedなら対象の本が定額で読み放題 [広告]
対象本には、プログラミングなどのIT関連本、マンガ、雑誌、ビジネス書などがたくさんあります!

Spring Bootの記事

Spring Boot2で日記投稿ウェブアプリ開発入門トップページ
Spring Bootでシンプルな日記投稿ウェブアプリの開発しながら、入門者・初心者にもわかりやすいように説明しています。DBを使用して、新規投稿、編集、削除、一覧表示という基本的なCRUD機能を備えたアプリです。