(第3回) DBのテーブル設計、エンティティクラスと初期データの作成【Spring Boot2で日記ウェブアプリ】

Spring Boot2.7で日記投稿ウェブアプリ入門の第3回です。アプリで使用するDBのテーブル構成を決めたり、エンティティクラスの作成、データ初期化のdata.sqlの作成、SQLログとH2 Consoleで動作確認をします。Spring Bootの初心者・入門者の方は、参考にしてみてください。

動作環境と今回の目的

最終更新日:2022/11/25

前回は、SQLログ出力の設定とDBの接続設定をしました。DBはH2を使用し、SQLログ出力にはLog4jdbc-log4j2を使用する事にしました。
(第2回) DB(H2)の接続設定とSQLログ出力の設定【Spring Boot2で日記ウェブアプリ】

今回は、Spring Bootの日記ウェブアプリのDBのテーブル構成についての説明と、エンティティクラスの作成、初期データのSQLファイル(data.sql)の作成、そしてアプリを起動してSQLログやH2 Consoleの動作確認などをやっていきます。

◾️動作環境やバージョン情報です。
OS:macOS Big Sur(バージョン11.7.1)
開発環境:Eclipse(Pleiades All in One、4.16(2020-06)、Java Full Edition版)
Spring Bootバージョン:2.7.5
->2.4でも確認済み
Java:11
データベース:H2

今回作成するファイル

今回作成するファイルを先に書いておきます。

今回新規に作成するファイルは「Diary.java」「data.sql」の2つです。

Diaryテーブル - DBのテーブル構成

まず、日記投稿ウェブアプリで使うDBのテーブル構成についてです。とは言ってもシンプルなアプリなので、作成するテーブルは、投稿した日記本文をデータ保存するためのDiaryテーブル1つです。

そのDiaryテーブルの構成です。

フィールド名 データ型 備考(制約とか)
ID id integer プライマリーキー
日記本文 bodytext varchar(255) not null
投稿日時 create_datetime timestamp not null

Diaryテーブルはタイトルは無く日記本文だけで、日記本文は長文にならないように文字数制限をつけています。
日記というよりメモ書き・ツイートのようなシンプルさです。

エンティティクラスDiaryの作成

次にDiaryテーブルを想定して、JavaのエンティティクラスDiary.javaを作成します。

com.example.demoパッケージ内にDiary.javaを作成して編集します。

package com.example.demo;

import java.time.LocalDateTime;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
//@Table(name = "diary")	//指定しないと生成されるテーブルはクラス名と同じdiary
public class Diary {
    public Diary(String bodytext, LocalDateTime createDatetime) {
        this.bodytext = bodytext;
        this.createDatetime = createDatetime;
    }
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;
    @Column(nullable = false)
    private String bodytext;
    //投稿日時
    @Column(name = "create_datetime", nullable = false)
    private LocalDateTime createDatetime;
}

まずDiaryクラスはエンティティクラスである事を示すために@Entityを付けています。
そして、テーブル設計に合わせて、id,bodytext,createDatetimeの3つのフィールドがあります。

idフィールドに付けている@Idは、エンティティの主キーを示しています。
また、@GeneratedValueは主キーが自動採番される事、strategy=GenerationType.IDENTITYは採番方法を示しています。

createDatetimeに付けている@Columnは、フィールドに対する設定や制約をしています。
「name = "create_datetime"」はテーブルにあるフィールド名を"create_datetime"に設定し、「nullable = false」はフィールドの値をnull禁止(not null)にしています。
@Columnを付けていないと、エンティティのフィールド名とテーブルのフィールド名は同じになります。

@Data、@NoArgsConstructor、@AllArgsConstructorは、Lombokのアノテーションです。
これらのLombokのアノテーションを付ける事によって、セッター・ゲッター・コンストラクタなどのコードをプログラマが書かなくても自動生成してくれるので便利です。

Eclipseの場合、アウトラインビューでクラスにあるフィールド、メソッド、コンストラクタの一覧が見れますので、ここでLombokによって自動生成されたメソッドやコンストラクタも確認できます。

また、エンティティクラス名と実際のDBのテーブル名を別にしたいなら、@Tableを使います。今回は同じ名称でいいので、@Tableは使っていません。

Spring Data JPAとエンティティクラス

これでSpring Bootアプリを起動すると、起動時にエンティティクラスDiaryからDB(データベース)にdiaryテーブルが自動生成されます。 エンティティクラスからテーブルを自動生成してくれるのはSpring Data JPAの機能です。
(H2データベースにdiaryテーブルが作成されたかどうかの確認は、H2 Consoleでできます。H2 Consoleについてはこの後に説明しています。)

エンティティクラスからテーブルを自動生成しないで、テーブル生成のSQLファイル(DDL、schema.sql)を自分で用意してテーブルを作る事もできます。(MySQLやPostgreSQLなどのDBを使うなら、予めテーブルを作っておくのが普通かもしれません。)

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

data.sql、データ初期化のSQLを作成

エンティティクラスDiaryによってdiaryテーブルが準備できたので、今度はテスト的に初期データをdairyテーブルに作ります。

Spring Bootでは、プロジェクトのsrc/main/resourcesディレクトリ下にdata.sqlファイルを作成し、このファイルにDML(Data Manipulation Language)のInsert SQLを書いておけば、アプリ起動時に自動でdata.sqlをロードして実行してます。

それでは実際にdata.sqlファイルを作成します。
(*data.sqlファイルでコメントアウトしたい場合は、行頭に"--"を付けます)

INSERT INTO diary(bodytext, create_datetime) VALUES('今日は晴れ。コメント1', LOCALTIME());
INSERT INTO diary(bodytext, create_datetime) VALUES('comment 2', LOCALTIME());
INSERT INTO diary(bodytext, create_datetime) VALUES('こめんと3', LOCALTIME());
上で作成したエンティティクラスDiaryに合わせてsqlを作成しました。diaryテーブルに3レコード作成するsqlです。
LOCALTIME()は現在日時を生成します。

また、application.propertiesファイルにspring.jpa.defer-datasource-initializationプロパティの設定を1行追加します。

#Spring Bootのバージョン2.5以上ならこの1行が必要
spring.jpa.defer-datasource-initialization=true

このspring.jpa.defer-datasource-initializationプロパティは、Spring Bootのバージョン2.5以上に必要な設定で、2.4以下なら書く必要は無いです。

これは、Spring Bootの2.4から2.5へのバージョンアップ時にdata.sql周りの初期化スクリプトの仕様変更があったみたいで、2.4系まではエンティティクラスがある場合は、エンティティクラスからDBのテーブルが生成された後に、data.sqlにあるInsert SQLが実行される流れだったのが、2.5系になってからはその順番が逆になり、エンティティクラスからテーブルが生成される前に、data.sqlにあるSQLが実行されます。

ですので、バージョン2.5以上ではDBテーブルが無いのにdata.sqlにあるInsert SQLが実行されてエラーが発生してしまうのですが、上記の「spring.jpa.defer-datasource-initialization=true」を設定する事で、エンティティクラスがテーブルを生成した後に、data.sqlを実行する事ができます。

この問題については別ページでもう少し詳しく説明しているので、よければ参考にしてください。
Spring Boot2.4から2.7へバージョンアップ時にエラー発生

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

Spring Bootのバージョン2.5以上でDBテーブルの作成(DDL)をschema.sqlでする

上記で、Spring Data JPA機能でDiaryエンティティクラスからdiaryテーブルを自動生成して、data.sqlにあるInsert SQLでデータ初期化を行う。ただSpring Bootのバージョン2.5以上ではspring.jpa.defer-datasource-initializationプロパティを設定する必要があると説明しました。

もう1つの方法として上でも少し触れましたが、テーブル生成はDiaryエンティティクラスで行わず、予め用意したSQLファイル(schema.sql)で行う方法もあります。DBのテーブル生成(DDL)はschema.sqlファイルに書いたcreate SQLで行い、データ初期化(DML)はdata.sqlのInsert SQLで行います。この方法であれば、バージョン2.5以上でもspring.jpa.defer-datasource-initializationプロパティを指定する必要はありません。

実際にschema.sqlでdiaryテーブルを作成する場合です。src/main/resourcesディレクトリ下にschema.sqlファイルを作成して編集します。

create table diary (id integer generated by default as identity, bodytext varchar(255), create_datetime timestamp not null, primary key (id));

ただし、schema.sqlでテーブル作成する場合もDiaryエンティティクラスがdiaryテーブルを自動生成しようとするので、エンティティクラスが自動でテーブル生成するSpring Data JPAの機能を無効化する必要があります。そのためにapplication.propertiesファイルにspring.jpa.hibernate.ddl-autoプロパティの設定を1行追加します。

#エンティティクラスによるテーブルの自動生成を無効にする
spring.jpa.hibernate.ddl-auto=none

また、DBのテーブル生成(DDL)とデータ初期化(DML)のSQLファイルを明示して指定したい場合、application.propertiesファイルで「spring.sql.init.*」プロパティを指定します。

spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath:schema.sql
spring.sql.init.data-locations=classpath:data.sql
spring.sql.init.schema-locationsプロパティでDDLファイル、spring.sql.init.data-locationsでDMLファイルを指定します。

DBのテーブル作成とデータ初期化の方法を2つ説明しましたが、どちらの方法を選択するかは各自で選択してください。
本記事の今後の説明は、エンティティクラスDiaryからdiaryテーブルを自動生成する方法で話を進めていきたいと思います。(どちらの方法でも大した差はないと思いますが、SQLログの出力が少し異なってくると思います。)

アプリの実行とSQLログとH2 Consoleで動作確認

それではSpring Bootのアプリを起動して、動作確認をしてみます。

前回のページでは、DBの接続設定とLog4jdbc-log4j2でSQLログの出力設定をしましたので、確認しておきたい点は、エンティティクラスDiaryからdiaryテーブルが生成される事、初期データ生成のdata.sqlのSQLが実行されてデータが生成される事の2点です。

実際にできているかどうかは、Eclipseのコンソールビューに出力されるSQLのログとH2のブラウザ管理ツールH2 Consoleの2つで確認していきます。

それでは実際にアプリを起動して、起動時にコンソールビューに出力されたログの一部です。

2022-11-24 01:20:09.345  INFO 59772 --- [  restartedMain] jdbc.sqlonly                             : drop table if exists diary CASCADE 

2022-11-24 01:20:09.350  INFO 59772 --- [  restartedMain] jdbc.sqlonly                             : create table diary (id integer generated by default as identity, bodytext varchar(255) not 
null, create_datetime timestamp not null, primary key (id)) 

2022-11-24 01:20:09.363  INFO 59772 --- [  restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2022-11-24 01:20:09.371  INFO 59772 --- [  restartedMain] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2022-11-24 01:20:09.422  WARN 59772 --- [  restartedMain] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2022-11-24 01:20:09.713  WARN 59772 --- [  restartedMain] ion$DefaultTemplateResolverConfiguration : Cannot find template location: classpath:/templates/ (please add some templates, check your Thymeleaf configuration, or set spring.thymeleaf.check-template-location=false)
2022-11-24 01:20:09.780  INFO 59772 --- [  restartedMain] jdbc.sqlonly                             : INSERT INTO diary(bodytext, create_datetime) VALUES('今日は晴れ。コメント1', LOCALTIME()) 

2022-11-24 01:20:09.786  INFO 59772 --- [  restartedMain] jdbc.sqlonly                             : INSERT INTO diary(bodytext, create_datetime) VALUES('comment 2', LOCALTIME()) 

2022-11-24 01:20:09.786  INFO 59772 --- [  restartedMain] jdbc.sqlonly                             : INSERT INTO diary(bodytext, create_datetime) VALUES('こめんと3', LOCALTIME()) 
少し見づらいですが、まず最初の方で"create table diary・・・"というSQLがあり、diaryテーブルが生成されています。
そしてその後に、"INSERT INTO diary・・・"というSQLが3回実行されて、初期データ生成をしています。
これは期待通りのSQLログの出力結果です。

次に、H2の操作・管理ツールH2 Consoleで確認します。H2 ConsoleのURLは「http://localhost:8080/h2-console 」です。アクセスしたら下の画像のようなログイン画面が表示されるはずです。
(*初期表示画面は英語ですが、左上のボックスから日本語に変更できます。)
H2 Consoleログイン

ログイン画面の各入力欄は、application.propertiesファイルで設定したDB接続設定と対応しています。
前回のページで、DB接続設定をapplication.propertiesに書きましたので、ログイン画面の各入力欄には以下のように入力して接続ボタンを押せばログインできるはずです。

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

ログインすると、下のようなH2 Consoleの管理画面が表示されます。ここで、Spring BootアプリのDBにあるテーブルやテーブル内のデータが確認できます。
H2 Console管理画面、select SQL

管理画面を見ると、左サイドバーからDIARYテーブルができている事がわかります。
そして、そのDIARYテーブルを選択すると全件取得のselect sqlがSQL入力欄に表示され、実行ボタンを押すとSQLの全件取得結果が表示されているのがわかります。

この出力結果は、上で書いたコンソールビューの実行されたSQLログと一致するので、これも期待した通りの結果です。

ちなみにH2 Consoleで実行ボタンを押した後にコンソールビューを確認すると、selectクエリとその結果がログ出力されている事がわかります。

2020-10-25 20:34:55.108  INFO 85563 --- [nio-8080-exec-7] jdbc.sqlonly                             : SELECT * FROM DIARY 

2020-10-25 20:34:55.116  INFO 85563 --- [nio-8080-exec-7] jdbc.resultsettable                      : 
|---|------------|--------------------|
|id |bodytext    |create_datetime     |
|---|------------|--------------------|
|1  |今日は晴れ。コメント1 |2020-10-25 19:58:49 |
|2  |comment 2   |2020-10-25 19:58:49 |
|3  |こめんと3       |2020-10-25 19:58:49 |
|---|------------|--------------------|

コンソールビューでのselectクエリの結果表示が必要ないのなら、前回のページでも説明しましたが、application.propertiesファイルで"logging.level.jdbc.resultsettable=off"にすればできます。

H2 ConsoleではDBのデータの確認だけでなく、insertやdeleteなどのSQLを実行してデータ生成や削除もできで便利ですので色々と試してみてください。

最後に、次回

以上今回は、Spring Bootの日記投稿ウェブアプリのDBのテーブル構成についての説明や、Diaryエンティティクラスの作成、データ初期化用のdata.sqlの作成、そしてアプリを起動してSQLログとH2 Consoleの動作確認などについて説明しました。

SQLログとH2 ConsoleでのDB管理・操作は、Spring Bootアプリを開発する時のプログラマの大きな手助けになると思います。

そして次回は、今回でdiaryテーブルができたので、dairyテーブルのCRUD操作をするためにリポジトリインタフェースを開発していきます。

(第4回) テーブルのCRUD操作のためにリポジトリインタフェースの作成【Spring Boot2で日記ウェブアプリ】
Spring Bootの日記投稿ウェブアプリ開発入門トップ