JPAは組み込みDBを利用する際にテーブルを自動生成する
0. tl;dr
- JPAは組み込みDBを利用している場合にEntityの情報などからDBとtableを自動で生成する
- application.properties(application.yaml)のspring.jpa.hibernate.ddl-autoというプロパティで自動生成する / しないが制御される
1. はじめに / 背景
Spring Bootを利用したプロジェクトにおいて、Integration Testの実装の際にH2(組み込みのin-memory DB)とJPAを利用しているのですが、H2の初期化のために書いていたDDLのスキーマと、JPA用のEntityのスキーマの整合性が取れていないにも関わらず、Testがpassしていてファ!?ってなりました。DatabaseのTableと、JPAのために記述しているEntityとの間で整合性が取れないとapplication contextの生成に失敗してしまうはずなのですが、application contextがうまく生成されるだけでなく、JPA経由でDBに値を入れたり、参照したり、というのが普通に動いてしまってました。
2. spring.jpa.hibernate.ddl-autoの設定について
結論としては、application.yamlで制御できるhibernateの設定で、自動でテーブルが生成される設定がonになっていたのが原因でした。(そんな設定があるの知らなかった)
以下、Documentより引用
By default, JPA databases are automatically created only if you use an embedded database (H2, HSQL, or Derby). You can explicitly configure JPA settings by using
spring.jpa.*
properties. For example, to create and drop tables you can add the following line to yourapplication.properties
:
spring.jpa.hibernate.ddl-auto=create-drop
(DeepL訳)デフォルトでは、JPAデータベースは、組み込みデータベース(H2、HSQL、Derby)を使用する場合にのみ自動的に作成されます。JPAの設定は、spring.jpa.*プロパティを使用して明示的に設定することができます。例えば、テーブルの作成と削除を行うには、application.propertiesに次の行を追加します。
JPAはH2などの組み込みデータベースを検出すると自動でデータベース(とテーブル)を生成してくれます。
以下はやや古い資料で正確性が怪しいですが、組み込みDBだとdefaultでcreate-dropが設定されているようです。create-dropではその名の通り、アプリケーション作成時にEntityに対応するテーブルがなければ作成し、セッション終了時にスキーマを削除します。
組み込みDBが検出されない場合はdefaultでnoneが設定されるので何もしません。
3. どう設定するか
下記のようにapplication.yamlでcreate teble文を記述したsqlへのパスを指定してtableを初期化するアプローチをいままではとっていました。
spring: sql: init: schema-locations: classpath:h2/schema.sql
DatabaseのDDLを管理しているリポジトリは別で存在し、schemaが更新される際にJPAのEntityも更新しますが、その際にH2の初期化用のDDLも更新する必要が出てきて、DDLの二重管理(場合によってはn重管理)みたくなってめんどくさい思いもあります。RepositoryのIntegration Testという文脈で捉えるなら初期化用のDDLを定義してもしなくても、依存はリポジトリの中に閉じていて本番のDBの状態からは切り離されてしまいます。
どうせ外部環境に繋がないなら自動生成をonにして、初期化用のddlなんて用意しなくて良いのでは?というお気持ちになりました(ライブラリの作者もそう思ったからdefault設定しているのでしょうし)
また、JPAの設定と実際のDBのスキーマの齟齬から疎通で失敗することの検証に関しては後続で結合試験を実施すれば検知できる課題だと思うので、個人的には自動生成に頼ってしまってDDLの管理はサボりたいなと感じました。
以上
4. Reference
spring.jpa.hibernate.ddl-auto に設定可能な値 - Qiita