H2メモリDBで体験する!Spring Bootのデータ保存と読み出し【Spring Boot】

  • LINEで送る
H2メモリDBで体験する!Spring Bootのデータ保存と読み出し

「まずはDB連携を体験したい」「MySQL を入れる前に動くところを確認したい」そんな時に最強の相棒が H2 Database(インメモリDB) です。今回は Spring Boot に H2 を組み込み、テーブル作成 → データ投入 → 取得/登録/更新/削除 までを最短ルートで体験します。初心者が迷いやすい設定ポイントも紹介しながら、一緒に進めていきましょう。

H2データベースとは何か

  • インメモリ型のデータベースで、アプリケーションが起動している間だけデータが保持されます。アプリを停止するとデータは消えますが、逆に「試すだけ」「一時的な確認」といった場面にはとても便利です。
  • インストール不要で、依存関係にH2を追加するだけですぐに利用可能。開発環境を汚さないのもメリットです。
  • ブラウザから中身を直接見られるH2コンソールが付属しており、SQLを書いて確認する練習にも使えます。
  • モード切替が豊富で、PostgreSQLやMySQLなど主要なRDBの方言をシミュレーションできます。これにより、本番用DBに近い挙動を試すことが可能です。
  • 学習や動作確認に最適。DBをインストールできない環境や、チーム開発で素早く検証したいときに活用されています。

H2データベースの環境を整える

Spring Initializrからプロジェクトを作成する方法

  1. Spring Initializr にアクセス。
  2. Project: Gradle Project を選択。
  3. Language: Java
  4. Spring Boot: 最新の安定版。
  5. Dependencies に以下を追加:
    • Spring Web
    • Spring Data JPA
    • H2 Database
  6. 生成されたZipをダウンロードし、IntelliJやEclipseにインポートします。

Gradle依存関係にH2とJPAを追加

build.gradle に自動生成されていますが、最低限以下が入っていればOKです。

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.5.5'
	id 'io.spring.dependency-management' version '1.1.7'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
description = 'Demo project for Spring Boot'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(17)
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	runtimeOnly 'com.h2database:h2'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
	
	compileOnly("org.projectlombok:lombok:1.18.38")
	annotationProcessor("org.projectlombok:lombok:1.18.38")

	testCompileOnly("org.projectlombok:lombok:1.18.38")
	testAnnotationProcessor("org.projectlombok:lombok:1.18.38")
}

tasks.named('test') {
	useJUnitPlatform()
}

Mavenプロジェクトを選んだ場合は pom.xml に以下の依存関係を追加すれば同じ環境になります。

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  
  <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
  </dependency>
  
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

application.yml の基本設定

src/main/resources/application.yml を作成し、H2を有効化します。

spring:
  application:
    name: demo
  datasource:
    url: jdbc:h2:mem:demo;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password:
    driver-class-name: org.h2.Driver
  jpa:
    defer-datasource-initialization: true
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true
  h2:
    console:
      enabled: true
      path: /h2-console

application.ymlでの設定項目詳細

  • spring.datasource.url:jdbc:h2:mem:demo;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    • インメモリDBをdemoとして作成。PostgreSQLモードで動作。識別子を小文字化。アプリ動作中は保持し、終了時のみ削除。
  • spring.datasource.username: sa
    • H2のデフォルトユーザー。学習用なのでパスワードなしで利用可能。
  • spring.datasource.password: 空欄
    • 学習用途のため空で問題なし(実運用では設定推奨)。
  • spring.datasource.driver-class-name: org.h2.Driver
    • JDBCドライバクラス。自動解決されるが明示するとわかりやすい。
  • spring.jpa.hibernate.ddl-auto: update
    • スキーマを自動更新。学習用に便利。実務ではnoneやマイグレーションツールを使用。
  • spring.jpa.show-sql: true
    • 実行SQLをログに出力。学習・デバッグ向け。
  • spring.jpa.properties.hibernate.format_sql: true
    • ログ出力されるSQLを整形して見やすく表示。
  • spring.h2.console.enabled: true
    • H2コンソールを有効化。ブラウザでDB確認が可能。
  • spring.h2.console.path: /h2-console
    • H2コンソールへのアクセスパス。起動後に http://localhost:8080/h2-console で利用可能。

よくあるつまずき

  • JDBC URLの不一致:コンソールのURLとapplication.ymlのURLが一致していないと接続できません。
  • 大文字/小文字問題:H2はクォートの有無で識別子の扱いが変わります。DATABASE_TO_LOWER=TRUEで揺れを吸収。
  • テーブルが見つからない@Entity/@Idの付け忘れ、またはddl-autonone/validateのままになっている可能性。

発展メモ

  • ファイルモードへ切替:データを保持したい場合は jdbc:h2:file:~/demo のようにmemfileへ。初学段階では不要。
  • 別RDB方言の試用:本番がMySQLなら MODE=MySQL を試すと方言差の検証ができます。

data.sql を“後から”流す(おすすめ)

 Spring Boot 2.5以降では、data.sql がHibernateより先に実行される仕様になりました。そのため、まだテーブルが作られる前にINSERTが走り、エラーになる場合があります。これを防ぐためには次の設定を追加します。

spring:
  jpa:
    defer-datasource-initialization: true

 これにより、Hibernateでテーブル作成 → その後にdata.sqlを実行 という順序になります。これで初期データが正しく投入されます。

H2データベースを利用した最小構築手順

 環境の構築ができたところで、実際にコードを書いてみましょう。今回はBookのレコードをH2データベースに登録・消去・検索を行なってみたいと思います。

Entityの構築

 Entityは、Javaのクラスをデータベースの表(テーブル)と対応させるためのマッピングオブジェクトです。そのクラスのインスタンスは、そのテーブルの1行(レコード)を表します。つまり「本」を表す Book クラスを Entity にすると、book テーブルの各行(タイトルや著者の情報)を Java オブジェクトで扱えるようになります。実際にEntityクラスを構築してみます。

package com.example.demo;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Table(name = "book")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String title;

    private String author;

}

Repositoryインターフェースの作成

 Repository はデータベースにアクセスするための窓口で、SQLを書かずに標準的なCRUD操作や検索を行える仕組みです。JpaRepositoryをextendしていきます。

package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface BookRepository extends JpaRepository<Book, Long> {
    List<Book> findByTitleContainingIgnoreCase(String keyword);
}

初期データ投入方法

 Spring Boot では src/main/resources 配下に data.sql を置くと、アプリ起動時に自動的に読み込まれ実行されます。そのため INSERT 文を記述すれば起動時にデータが挿入されます。インメモリDBの場合はアプリを再起動するたびにテーブルが再作成されるため、毎回 data.sql の内容が流し込まれる点も学習に便利です。 src/main/resources/data.sql

INSERT INTO book (title, author) VALUES ('Spring入門', 'Taro');
INSERT INTO book (title, author) VALUES ('はじめてのH2', 'Hanako');

H2コンソールの利用方法

 data.sqlをプロジェクトに追加したらまずは動かしてみましょう。アプリを起動後下記のURLに飛ぶとH2コンソールの接続画面が表示されます。

http://localhost:8080/h2-console

下記の画像はH2コンソールの接続画面になります。

H2コンソール

この画面が開いたらConnectを押すことでH2コンソールの画面が開きます。

H2コンソールでできること

 H2データベースでは下記のことができます。色々触ってみてください。

  • SQL文を入力して実行し、結果を表形式で確認できます。
  • 左側にテーブル一覧が表示され、クリックで内容を確認できます。
  • テーブル定義(カラム名や型)の確認も可能です。
  • INSERT/UPDATE/DELETE などを直接実行してデータを操作できます。
  • 学習用途であれば、GUI的にDBの状態を覗ける“簡易的な管理ツール”として活用できます。

Service層とController層の実装

 実際にH2データベースと接続できたら、Service層とController層を実装してDBを更新していきます。本記事ではService層とController層に実装を分割していますが、ではなぜ分割する必要があるのでしょうか?それには下記のような理由があります。

ここでの役割分担(なぜ分けるの?)

  • Service層:アプリの業務ロジックの中心。トランザクション境界(@Transactional の付与)、バリデーション、複数リポジトリの編成、外部APIとの連携、ドメインルールの実装などを担います。入出力形式(HTTP/DB)に依存しない純粋なロジックを置くのが理想です。
  • Controller層HTTPの入出力担当。URLへのマッピング、クエリ/パス/ボディの受け取り、DTO↔ドメインの変換、適切なHTTPステータスコードの返却、例外のハンドリング(@ControllerAdvice 連携)などを行います。ロジックは極力書かず、Serviceを呼び出す薄い層に保ちます。

詳しくは下記の記事でも紹介しています。

業務ロジックを搭載するService層の実装

 まずは業務ロジックを搭載するService層を実装していきます。

package com.example.demo;

import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class BookService {

    private final BookRepository repo;

    /** 一覧取得 */
    @Transactional(readOnly = true)
    public List<Book> findAll() {
        return repo.findAll();
    }


    /** IDで1件取得(見つからなければ null) */
    @Transactional(readOnly = true)
    public Book findById(Long id) {
        return repo.findById(id).orElse(null);
    }


    /** タイトル部分一致検索 */
    @Transactional(readOnly = true)
    public List<Book> searchByTitle(String keyword) {
        return repo.findByTitleContainingIgnoreCase(keyword);
    }


    /** 登録 */
    @Transactional
    public Book create(String title, String author) {
        return repo.save(new Book(null, title, author));
    }


    /** 更新(存在しない場合は null) */
    @Transactional
    public Book update(Long id, String title, String author) {
        Optional<Book> opt = repo.findById(id);
        if (opt.isEmpty()) return null;
        Book b = opt.get();
        if (title != null && !title.isBlank()) b.setTitle(title);
        if (author != null) b.setAuthor(author);
        return repo.save(b);
    }


    /** 削除 */
    @Transactional
    public void delete(Long id) {
        repo.deleteById(id);
    }
}
メソッド役割
findAll()全ての本を一覧で取得する
findById(Long id)指定IDの本を1件取得する(存在しなければ null
searchByTitle(String keyword)タイトルに部分一致する本を検索する
create(String title, String author)新しい本を登録する
update(Long id, String title, String author)指定IDの本を更新する(存在しなければ null
delete(Long id)指定IDの本を削除するメソッド名

@Transactional とは?

Spring が データベース処理をまとめて管理する仕組み です。

  • メソッド全体を「1つの処理のかたまり(トランザクション)」として実行する
  • 成功すれば コミット(DBに反映)、失敗すれば ロールバック(処理をなかったことにする)
  • readOnly = true を付けると 読み取り専用 になり、無駄な更新チェックを省けて効率的

リクエストを受け付けるController層の実装

 続いてリクエストを受け付けて、Service層に渡すControllerのBookControllerを実装します。

package com.example.demo;

import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/books")
@RequiredArgsConstructor
public class BookController {

    private final BookService service;

    @GetMapping
    public List<Book> list() { return service.findAll(); }


    @GetMapping("/{id}")
    public Book get(@PathVariable Long id) { return service.findById(id); }


    @PostMapping
    public Book create(@RequestBody Map<String, String> body) {
        return service.create(body.get("title"), body.get("author"));
    }


    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id) { service.delete(id); }
}

REST APIでのCRUD動作確認

 Controllerが実装できたら実際にリクエストを送ってみましょう。今回はcurlコマンドを使ってリクエストをしてみました。

# 全件取得
curl http://localhost:8080/api/books

# 登録
curl -X POST http://localhost:8080/api/books \
-H "Content-Type: application/json" \
-d '{"title":"Spring Boot実践", "author":"Satoshi"}'

# 取得
curl http://localhost:8080/api/books/1

# 削除
curl -X DELETE http://localhost:8080/api/books/1

 実装に問題がなければCRUD処理ができるはずです。DBの中身をコンソールから確認しながらコマンド実行してみてください。

まとめ

 今回の記事では、Spring Boot と H2 データベースを使い、インストール不要で軽量な開発環境を構築しました。H2 はインメモリ型のため再起動ごとにリセットされ、学習や試行錯誤に最適です。Repository はinterfaceだけでよく、Spring Data JPA が自動で実装を用意してくれるため、SQL を書かずに CRUD 操作が可能です。さらに H2 コンソールによるデータ確認や、data.sql を利用した初期データ投入の仕組みも学びました。これらは今後 MySQL などの本格的なDBに移行する際にも役立ちます。

 次回は、この CRUD 処理にバリデーションや例外処理を加え、実務に近い堅牢なアプリケーション作成に進みます。

最新の投稿

SNSでもご購読できます。

コメントを残す