DockerでSpring Boot環境を構築する

DockerでSpring Boot環境を構築する Java

Docker環境にて、Spring Bootを利用できるようにします。
Spring Initializrで作成したプロジェクトを、Docker上でビルド、起動します。
IDEはVS Codeを利用して、デバッグもできるようにします。
ビルドには、Gradle を使用します。

環境

  • Java:17
  • Spring Boot:3.0.1
  • MySQL:8.0

ディレクトリ構成

以下のディレクトリ構成としています。

.
├── docker-compose.yml
├── infra
│   ├── mysql
│   │   ├── Dockerfile
│   │   └── my.cnf
└── workspace (Spring Bootプロジェクトのディレクトリ)

Docker環境

まず、Docker環境を作成します。
構築するサーバーはアプリケーションサーバー(app)、データベースサーバー(db)の2つになります。

docker-compose.yml を作成します。

version: "3.9"
services:
  app:
    image: eclipse-temurin:17
    ports:
      - 8080:8080
    volumes:
      - ./workspace:/workspace
    working_dir: /workspace
    tty: true

  db:
    build: ./infra/mysql
    volumes:
      - db-store:/var/lib/mysql
    ports:
      - 3306:3306

volumes:
  db-store:

アプリケーションサーバー(app)コンテナ

アプリケーションサーバーについては、Docker HubがOpenJDKイメージの廃止を決定したそうなので、Eclipse Temurinの公式イメージを使用しています。

▼廃止について参考

▼Eclipse Temurinの公式イメージ
https://hub.docker.com/_/eclipse-temurin

データベースサーバー(db)コンテナ

データベースサーバーについては、Dockerfileを作成します。

FROM mysql/mysql-server:8.0

ENV MYSQL_DATABASE=spring \
  MYSQL_USER=spring \
  MYSQL_PASSWORD=secret \
  MYSQL_ROOT_PASSWORD=secret \
  TZ=Asia/Tokyo

COPY ./my.cnf /etc/my.cnf
RUN chmod 644 /etc/my.cnf

設定は以下のようにしています。

[mysqld]
# default
skip-host-cache
skip-name-resolve
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
secure-file-priv = /var/lib/mysql-files
user = mysql

pid-file = /var/run/mysqld/mysqld.pid

# character set / collation
character_set_server = utf8mb4
collation_server = utf8mb4_ja_0900_as_cs_ks

# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# Error Log
log-error = mysql-error.log

# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0

# General Log
general_log = 1
general_log_file = mysql-general.log

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

コンテナの起動

各ファイルが作成できたら、各コンテナを起動します。
ここでは、まだ動かすものがないので、コンテナが正常に起動できていればOKとします。

Spring Bootのプロジェクトを作成

Spring Initializrを利用すると、簡単にSpring Bootのプロジェクトを作成することができるので、利用します。
以下のようにしました。

Spring Initializr

依存関係は以下を選択しました。

  • Spring Boot DevTools
  • Lombok
  • Spring Web
  • Thymeleaf
  • Spring Data JPA
  • MySQL Driver

「GENERATE」ボタンをクリックすることで、ダウンロードできるので、展開したものをworkspace配下に配置します。

コンテナ上でビルド

コンテナ上でビルドを行います。
アプリケーションサーバーコンテナに入り、実行します。

$ pwd
/workspace
$ ll
total 36
drwxr-xr-x 11 root root  352 Jan  7 06:21 ./
drwxr-xr-x  1 root root 4096 Jan  7 06:22 ../
-rw-r--r--  1 root root  857 Jan  6 21:06 build.gradle
drwxr-xr-x  6 root root  192 Jan  7 05:38 .gradle/
drwxr-xr-x  3 root root   96 Jan  6 21:06 gradle/
-rwxr-xr-x  1 root root 8188 Jan  6 21:06 gradlew*
-rw-r--r--  1 root root 2838 Jan  6 21:06 gradlew.bat
-rw-r--r--  1 root root 1508 Jan  6 21:06 HELP.md
-rw-r--r--  1 root root   26 Jan  6 21:06 settings.gradle
drwxr-xr-x  4 root root  128 Jan  6 21:06 src/

$ ./gradlew build

ビルドしたところ、testタスクでエラーが発生しました。

> Task :test

DemoApplicationTests > contextLoads() FAILED
    java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:142
        Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:798
            Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:657
                Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:171
                    Caused by: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException at DataSourceProperties.java:182

1 test completed, 1 failed

> Task :test FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///workspace/build/reports/tests/test/index.html

JPAを使用しているため、DBの接続設定が必要です。
src/main/resources/application.properties にDBの接続情報を追加します。

spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://db:3306/${db.url:spring}
spring.datasource.username=${db.username:spring}
spring.datasource.password=${db.password:secret}
spring.jpa.hibernate.ddl-auto=update

DBの接続情報を追加することで、ビルドが成功になりました。

DB検索機能を作成する

JPAを利用して、DB検索機能を実装してみます。

モデルの実装

まず、モデルの実装です。
ユーザーを管理するusersテーブルとしています。

package com.example.demo.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;

@Entity
@Data
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(nullable = false)
    private String email;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String name;
}

コントローラーの実装

続いて、コントローラーの実装です。
データを取得する処理にはサービスクラスに実装しているので、コントローラーはリクエストの処理のみとしています。

package com.example.demo.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.demo.entity.User;
import com.example.demo.service.UserService;

@Controller
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping
    public String index(Model model) {
        List<User> users = userService.getUsers();
        model.addAttribute("users", users);
        return "users/index";
    }
}

サービスクラスの実装

続いて、サービスクラスの実装です。
サービスクラスでは、リポジトリクラスを通じて、DBからデータを取得しています。

package com.example.demo.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public List<User> getUsers() {
        return userRepository.findAll();
    }
}

リポジトリクラスの実装

続いて、リポジトリクラスの実装です。
リポジトリクラスでは、JpaRepositoryを継承するだけで、取得するロジックを実装する必要はありません。

package com.example.demo.repository;

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

import com.example.demo.entity.User;

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {}

ビューの実装

最後にビューの実装です。
HTMLは必要最低限にしていますが、取得したデータを一覧表示する実装にしています。

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8" />
</head>
<body>
    <table>
        <thead>
            <tr>
                <th>ID</th>
                <th>メールアドレス</th>
                <th>名前</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="user : ${users}" th:object="${user}">
                <td th:text="*{id}"></td>
                <td th:text="*{email}"></td>
                <td th:text="*{name}"></td>
            </tr>
        </tbody>
    </table>
</body>
</html>

ビルドと起動

実装が完了したら、ビルドとサーバーの起動を行います。

$ ./gradlew build
$ java -jar build/libs/demo-0.0.1-SNAPSHOT.jar

起動が完了したら動作確認をしますが、最初はデータが入っていないので、insert文で挿入しておきます。

INSERT INTO `spring`.`users`
(`id`,
`email`,
`name`,
`password`)
VALUES
(1,
'test@test.jp',
'テスト太郎',
'password');

URLは以下となります。
http://localhost:8080/users

以下のように、データが表示されることが確認できました。

検索機能

コンテナに接続する

ここまでで実行する環境ができたので、続いて、Dockerのコンテナに接続します。
コンテナに接続することで、ライブラリの参照やコード補完ができるようになり、デバッグが行えるようになります。
本記事ではVSCode のDev Containers を使用します。

▼公式ドキュメント
https://code.visualstudio.com/docs/devcontainers/containers

▼参考

コンテナに接続して、VSCodeのデバッグモードで起動することで、デバッグが行えます。

タイトルとURLをコピーしました