【初心者向け】 SpringBoot を使った WebAPIの開発を macOS Sonoma と Dockerコンテナ 環境でやってみよう!


記事の概要

この記事では、JavaのSpring Bootフレームワークを使って、Dockerコンテナで動作するWebAPIの開発環境の構築からアプリ開発、実行までの流れを紹介します。

この記事を参考に、WebAPI開発のスタートの一助となれば幸いです。


今回の記事の対象者

この記事は次のような方に向けています:

  • Spring Bootを使ってアプリ開発環境の構築をしたい方
  • Dockerコンテナを使ってサーバー(VPSやクラウド環境)を環境を統一したい方
  • ドメイン駆動設計に興味がある方
  • 生成AIにちょっと興味がある方

はじめに

普段は、JakartaEE使ってWebアプリやバックエンドのWebAPIの開発を行っていますが、もっと手軽なSpring Bootでアプリが作ってみたくなりました。

Spring Bootの母体となるSpring Frameworkは、歴史も長く人気のあるフレームワークで、豊富なライブラリを使った開発が出来る魅力があります。

Spring BootやSpring Frameworkについての詳細は、公式サイトをご覧下さい。


以前からドメイン駆動設計を適用した、拡張性や可読性が高いプログラムコードにも関心がありました。

ドメイン駆動設計は少し難易度が高い設計手法ですが、具体的なプログラムコードを書くことによって身近な手法にできると考えたので、今回はこれを使ってみようと思います。(平たく言うと、百聞は一見にしかず、ということです)


また、最近流行の生成系AIが開発作業にどれだけ役に立つの?

と言うのも興味があったので、開発の中で、生成AIをお試ししています。今回の生成AIは、JetBrains社製のIDEで利用出来るAI Assistantを使っています。


目次


コンセプト

まずは、本記事で構築する開発環境やアプリのコンセプトを紹介していきます。

  • Spring Bootプロジェクト※: Spring Bootを使ったディレクトリ構造やファイルの役割を理解し、効率的にプロジェクトを管理します。※アプリを開発するためのファイルやフォルダの集まり。
  • ビルドと依存関係管理: Gradleを使ってプロジェクトのビルドと依存関係を管理する。
  • コンテナ技術: DockerコンテナとDocker Composeを使って、アプリをコンテナ化し、開発および本番環境の差異をなくす。
  • API開発: Spring Bootを使って、WebAPIを作成し、他のシステムやクライアントとデータをやり取りする。
  • データベース操作: MyBatisを使って、データベースと連携する。
  • ドメイン駆動設計: ソフトウェア開発の効率や品質を向上させる。
  • 生成AI:Jetbrains社が提供する生成AIの機能をつかってコーディングの効率化出来る様にする
    • AI Assistant コメントの追加

【筆者の一言】
今回の開発環境は、IntelliJ IDEAやVS CodeなどのIDEに依存しない形で構築します。
まずはIDEを何も使わずに、プロジェクトディレクトリや設定ファイルを作成します。
その後、作成したプロジェクトディレクトリをIDEで開き、コーディングやビルドなどを行っていきます。

ソフトウェア・ハードウェア

今回の手順を実施するのに必要なツール、ライブラリ、端末は以下の通りです。

開発ツール

開発ツールとその公式サイトの一覧です。公式サイトを参照して事前に導入を実施してください。

ソフトウェア用途
IntelliJ IDEA CommunityJavaを中心としたプログラミングの統合開発環境 (IDE)
HomebrewmacOSおよびLinux向けのパッケージ管理システム
Eclipse TemurinJavaランタイム環境(JRE)および開発キット(JDK)のオープンソースディストリビューション
Gradleビルド自動化ツール。Javaプロジェクトのビルド、テスト、デプロイに使用される
pgAdmin4PostgreSQLデータベースの管理ツール
CurlデータをURLで指定された場所から転送するコマンドラインツール

ミドルウェア

SpringBootアプリを実行する主要なソフトウェアとその解説です。Docker Desktopは、事前にインストールしておいてください。PostgreSQLのインストールは、記事本編で解説します。

ソフトウェア概要
Docker Desktop 4.33Docker Desktopは、開発者がコンテナを使ってアプリケーションを簡単に構築、テスト、デプロイするためのツールです。最新バージョンでは、ファイル共有の強化、ビルドビューの改良などが行われています。
PostgreSQL 16PostgreSQLは、オープンソースのリレーショナルデータベース管理システム(RDBMS)で、拡張性と標準への準拠を重視しています。バージョン16では、クエリの並列処理や大量データの読み込み、論理レプリケーションの改善など、多くの機能強化が行われています。

ライブラリ

以下は、WebAPIを構成する主要なライブラリです。導入は、記事本編で解説します。

ソフトウェア概要
SpringBoot 3.3.2SpringBootは、Springフレームワークに基づくスタンドアロンのプロダクショングレードのアプリケーションを迅速に構築するためのフレームワークです。最新バージョンでは、CDSサポート、オブザーバビリティの向上、Docker Composeのサポートなど、多くの新機能と改善が含まれています。
MyBatis 3.5.14MyBatisは、SQL、ストアドプロシージャ、および高度なマッピングをサポートする持続層フレームワークです。最新バージョンでは、パフォーマンスの向上、バグ修正、およびセキュリティ改善が行われています。

端末

以下、今回の環境を構築する対象の端末スペックです。

項目詳細
ハードウェアApple Silicon M3, RAM 24GB
OSmacOS Sonoma 14.5

本記事で紹介するソフトウェアおよびツールは、筆者の個人的な使用経験に基づくものであり、公式のサポート外の設定や使用方法を含む場合があります。利用に際しては、公式サイトの指示およびガイドラインを参照し、自己責任で行ってください。


プロジェクトの準備

この章での最終的なプロジェクトのディレクトリ構造を示します。

my-springboot-app/
├── Dockerfile
├── build.gradle
├── docker-compose.yml
└── src
    ├── main
        ├── java
        │   └── com
        │       └── example
        │           └── demo
        └── resources

ステップ1: プロジェクトのディレクトリ構造を作成

まず、プロジェクトのディレクトリ構造を作成します。

mkdir my-springboot-app
cd my-springboot-app
mkdir -p src/main/java/com/example/demo
mkdir -p src/main/resources
touch build.gradle Dockerfile docker-compose.yml

ステップ2: build.gradle ファイルを設定

Gradleビルドツール用の設定ファイルです。プロジェクトの依存関係やプラグインを定義します。

build.gradleファイルに必要な依存関係とプラグインを追加します。

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

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '21'

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

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-docker-compose'
    runtimeOnly 'org.postgresql:postgresql'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

test {
    useJUnitPlatform()
}

解説

  • plugins:プロジェクトで使用するプラグインを指定します。ここでは、Spring BootやJavaを使うためのプラグインを指定します。
  • group:プロジェクトが所属するグループ名(任意の名前、javaのパッケージと同じにしておくと管理しやすい)です。
  • version:プロジェクトのバージョンです。
  • sourceCompatibility:Javaのバージョンを指定します。今回は最新のLTSバージョンである21を使用します。
  • repositories:依存関係をダウンロードするリポジトリを指定します。今回は、Maven central repositoryのみを指定しています。
  • dependencies:プロジェクトで使用するライブラリやフレームワークを指定します。

【参考】 今回Step1,2は、理解のため手作業で作成していますが、 Spring Initializrサイトで自動生成することもできます。上記のファイルと同等の内容を作成する場合は、以下のスクリーンショットを参考にしてください。

Spring Initializrサイトで自動生成する場合の設定内容

ステップ3: Dockerfile を設定

Spring BootアプリケーションのDockerイメージを作成し実行するためのファイルを作成します。

FROM openjdk:21-slim
COPY build/libs/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

解説

  • FROM:使用するベースイメージを指定します。ここではJava 21のイメージを使います。
  • COPY:ビルドされたアプリケーションをコンテナ内にコピーします。
  • ENTRYPOINT:コンテナが起動したときに実行されるコマンドを指定します。

ステップ4: docker-compose.yml を設定

複数のDockerコンテナを定義するファイルです。ここでは、PostgreSQLデータベースとSpring Bootアプリケーションのサービスを定義しています。

Docker Composeを使用してPostgreSQLデータベースとSpring Bootアプリケーションをセットアップします。

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data

  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/mydb
      SPRING_DATASOURCE_USERNAME: user
      SPRING_DATASOURCE_PASSWORD: password
    depends_on:
      - db

volumes:
  postgres-data:

解説

  • version:Docker Composeのバージョンを指定します。
  • services:起動するサービスを定義します。
    • db:PostgreSQLデータベースの設定です。
    • app:Spring Bootアプリケーションの設定です。
  • volumes:データを保存するためのボリュームを定義します。

ステップ5: IntelliJ IDEAで、プロジェクトディレクトリを開く

ここで、作成したプロジェクトディレクトリを、IntelliJ IDEA Community Edtionで読み込みます。

読込を行うと、自動でGradleのインストール、必要なライブラリのダウンロードなどを行ってくれます。


DDDを適用したアプリの設計

それでは、早速WebAPIの開発に入っていきたいですが、ドメイン駆動設計(以下、DDD)の思想に沿った内容となっているので、DDDの主要な概念と今回利用する重要なポイントをいくつか理解しておく必要があります。

ポイント①:ビジネスと技術の橋渡し

ビジネスの専門家(プロダクトマネージャ等)とエンジニアが共通の言語で話すことを促します。これにより、開発プロセスの中でエンジニアがビジネスの要求やルールを継続的かつ正確にソフトウェアに反映することができます。

DDDはビジネスと技術の橋渡しが出来ます

ポイント②:複雑なビジネスロジックの整理

複雑なビジネスロジックを「ドメインモデル(実装はエンティティです)」として整理し、個々の概念やその関係を明確に定義します。これにより、要件やルールが整理されてコードが分かりやすく保守しやすくなります。今回は、モデルがユーザーのみなので、ドメインモデルは作成しません。

ポイント③:柔軟な設計と拡張性

アプリケーションの設計を柔軟にし、将来的な変更や拡張に対応しやすくします。ドメインモデルやその周辺処理が明確に分かれているので、部分的な変更や技術の変更を局所的な変更に限定することが出来ます。また、ドメインロジックがそれぞれ独立しているため、ユニットテスト(個別の機能をテストすること)がしやすくなります。


今回のアプリとDDDの関係

作成するアプリケーションをドメイン駆動設計(DDD)の観点から解説します。対象は、ユーザー管理業務で、具体的な機能は下表の通りです。今回は、RESTful APIの方式を採用します。(URLでリソースを指定し、HTTPメソッドで動作を指定します)

ユーザ管理アプリの機能概要

機能HTTPメソッド, URL
ユーザー一覧を取得GET, /users
特定のユーザー情報を取得GET, /users/{id}
新規ユーザーを作成POST, /users
ユーザー情報を更新PUT, /users/{id}
ユーザー情報を部分更新PATCH, /users/{id}
ユーザーを削除DELETE, /users/{id}

DDDの基本的な概念を用いて、どのようにアプリケーションが設計されるかを見ていきましょう。

ビジネス要件を実現するための実装は、サービス、リポジトリ、エンティティに集約されます。それぞれの、責務は以下の通りです。ビジネスの専門家とエンジニアがこの設計を共有の言語を使って設計していきます。

  1. サービス(Service):UserServiceがビジネスロジックを実行します。今回はUserエンティティの操作を行います。他のリポジトリやエンティティを使った複雑な操作も行う事があります。
  2. エンティティ(Entity):Userエンティティがユーザー情報とその要件、ルールを実装します。ユーザ情報に関する要件やルールはこのエンティティに集約します。他のクラスに分散することはアンチパターンとなります。
  3. リポジトリ(Repository):UserRepositoryがユーザー情報の永続化を担当します。データベースの操作をエンティティやサービスなどから隠蔽します。具体的には、インタフェースのみとなります。データベース製品をPostgreSQLからMySQLに変更する場合はこのインタフェースを実装したクラスで吸収します。

次に、アプリケーションとして、クライアントからのアクセスを取り纏めるコントローラーやデータベースなどの実装技術は、上記の3つの概念の外側に実装されます。例えば、APIの提供がRestfulとは別になったり、データ永続化の方法が変わったりする場合にはこれらを修正し、上記の3つの概念には影響を与えないようにします。

DDDはビジネスロジックと周辺技術を分離できます

DDDを適用したアプリの開発

この章での最終的なプロジェクトのディレクトリ構造を示します。

my-springboot-app/
├── Dockerfile
├── build.gradle
├── docker-compose.yml
└── src
    ├── main
        ├── java
        │   └── com
        │       └── example
        │           └── demo
        │               ├── Application.java
        │               ├── controller
        │               │   └── UserController.java
        │               ├── entity
        │               │   └── User.java
        │               ├── mapper
        │               │   └── UserMapper.java
        │               ├── repository
        │               │   ├── UserRepository.java
        │               │   └── UserRepositoryImpl.java
        │               └── service
        │                   └── UserService.java
        └── resources
            ├── application.properties
            ├── com
            │   └── example
            │       └── demo
            │           └── mapper
            │               └── UserMapper.xml
            └── mybatis-config.xml

ステップ1: テーブルの作成

docker composeでdbセクションを実行します。

docker composeでdbサービスを起動する手順

実行するとPostgreSQLが起動します。以下の表示になったら正常に起動出来ています。

docker composeでdbサービスが起動していることが確認出来ます

次に、pgAdmin4でアクセスしてテーブルを作成してください。

テーブル作成用のDDLは以下の通りです。

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    phone_number VARCHAR(20) NOT NULL
);

ステップ2: ユーザ管理アプリの作成

src/main/java/com/example/demoディレクトリに以下のクラスを作成します。

Applicationクラス

Spring Bootアプリケーションのエントリーポイントです。

src/main/java/com/example/demo/Application.java

package com.example.demo;

// 必要なライブラリをインポートします。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// Spring Boot アプリケーションを宣言します。
@SpringBootApplication
public class Application {
    // アプリケーションの main メソッドです。
    // SpringApplication.run メソッドを呼び出して、アプリケーションの起動を開始します。
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

補足

SpringBootApplication というアノテーションは、これが Spring Boot アプリケーションのエントリーポイントであることを示します。このアノテーションにより、Spring Boot のコンフィギュレーションと起動の管理をすべて行います。 main メソッドはアプリケーションの起動ポイントです。ここでは、SpringApplication.run メソッドを呼び出して、Spring Boot アプリケーションを起動します。

MyBatisの設定ファイルを作成

ビジネスロジックで取り扱うuserエンティティと、その元となるデータベース(PostgreSQL)とのやり取り(SQL)を定義したファイル、との関連を定義しています。

src/main/resources/mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "<http://mybatis.org/dtd/mybatis-3-config.dtd>">
<configuration>
    <typeAliases>
        <typeAlias alias="User" type="com.example.demo.entity.User"/>
    </typeAliases>
    <mappers>
        <mapper resource="com/example/demo/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

MyBatisのマッパーXMLファイルを作成

userエンティティを生成・永続化するためのSQL文の定義を行います。MyBatisのマッパーインターフェースと対となるファイルです。

src/main/resources/com/example/demo/mapper/UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "<http://mybatis.org/dtd/mybatis-3-mapper.dtd>">
<mapper namespace="com.example.demo.mapper.UserMapper">
    <resultMap id="UserResultMap" type="com.example.demo.entity.User">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="phone_number" property="phoneNumber" />
    </resultMap>

    <select id="selectAll" resultMap="UserResultMap">
        SELECT * FROM users
    </select>

    <insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO users (name, phone_number) VALUES (#{name}, #{phoneNumber})
    </insert>

    <select id="selectById" parameterType="long" resultMap="UserResultMap">
        SELECT * FROM users WHERE id = #{id}
    </select>

    <update id="updateUser">
        UPDATE users SET name = #{name}, phone_number = #{phoneNumber} WHERE id = #{id}
    </update>

    <delete id="deleteUser" parameterType="long">
        DELETE FROM users WHERE id = #{id}
    </delete>
</mapper>

MyBatisのマッパーインターフェースを作成

マッパークラスの実体は、実行時にマッパーXMLを元MyBatisが生成します。

src/main/java/com/example/demo/mapper/UserMapper.java

package com.example.demo.mapper;

import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * ユーザー情報に対するデータベース操作を抽象化したマッパーインターフェースです。
 * このインターフェースのメソッドシグネチャは、MyBatisによってSQLクエリにマッピングされます。
 */
@Mapper
public interface UserMapper {

    /**
     * ユーザーの全てのデータを取得します。
     *
     * @return ユーザーのリスト
     */
    List<User> selectAll();

    /**
     * ユーザー情報を追加します。
     *
     * @param user 追加するユーザー情報
     */
    void insertUser(User user);
    /**
     * 引数で指定されたIDに一致するユーザー情報を取得します。
     *
     * @param id ユーザーID
     * @return IDが一致するUserエンティティ。存在しない場合はnull
     */
    User selectById(@Param("id") Long id);

    /**
     * ユーザー情報を更新します。
     *
     * @param user 更新するユーザエンティティ
     */
    void updateUser(User user);

    /**
     * 引数で指定されたIDに一致するユーザーを削除します。
     *
     * @param id 削除するユーザーのID
     */
    void deleteUser(@Param("id") Long id);
}

エンティティクラスを作成

ユーザーエンティティを定義するクラスです。このクラスは、Userという名前のエンティティを表現します。Lombokライブラリを利用して、getter、setter、equals、hashCode、及びtoStringメソッドを自動生成するために@Dataアノテーションを使用しています。

src/main/java/com/example/demo/entity/User.java

package com.example.demo.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

/**
 * Userエンティティクラス。
 * Lombokの@Dataアノテーションで自動的にgetter, setter等が生成される。
 */
@Data
public class User {

    /**
     * ユーザーID。
     */
    private Long id;

    /**
     * ユーザー名。
     * JSONで"name"というキーで出力/入力される。
     */
    private String name;

    /**
     * 電話番号。
     * JSONで"phoneNumber"というキーで出力/入力される。
     */
    private String phoneNumber;

    /**
     * 部分更新が有効であるか確認するメソッド。
     * ユーザー名または電話番号のみが設定されていれば有効。
     * JSON出力時には無視される。
     *
     * @return 部分更新が有効である場合はtrue、そうでない場合はfalse
     */
    @JsonIgnore
    public boolean isPartialUpdateValid() {
        return (this.name != null && this.phoneNumber == null) || (this.name == null && this.phoneNumber != null);
    }
}

レポジトリインターフェース

ユーザーエンティティのためのリポジトリインターフェースです。

src/main/java/com/example/demo/repository/UserRepository.java

package com.example.demo.repository;

import com.example.demo.entity.User;
import java.util.List;

/**
 * UserRepository インターフェース。
 * ユーザー関連のデータベースアクセスメソッドを定義しています。
 */
public interface UserRepository {

    /**
     * 全てのユーザーレコードを取得します。
     *
     * @return ユーザーのリスト
     */
    List<User> findAll();

    /**
     * 新しくユーザーを保存します。
     *
     * @param user 保存するユーザー
     */
    void save(User user);

    /**
     * 指定したIDのユーザーを取得します。
     *
     * @param id 取得するユーザーのID
     * @return IDに一致するユーザー、存在しない場合はnull
     */
    User findById(Long id);

    /**
     * ユーザー情報を更新します。
     *
     * @param user 更新するユーザー
     */
    void update(User user);

    /**
     * 指定したIDのユーザーを削除します。
     *
     * @param id 削除するユーザーのID
     */
    void delete(Long id);
}

レポジトリ実装クラス

ユーザーエンティティのためのリポジトリ実装クラスです。

src/main/java/com/example/demo/repository/UserRepositoryImpl.java

package com.example.demo.repository;

import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * UserRepositoryの具体的な実装クラスです。Springの@Repositoryアノテーションを使用して、
 * このクラスがデータリポジトリであることを示します。
 * UserMapperインスタンスを@Autowiredアノテーションを使って自動注入しています。
 * それぞれのメソッドは、UserMapperインターフェースの対応するメソッドを呼び出すことで
 * ユーザーに関するデータベース操作を実行します。
 */
@Repository
public class UserRepositoryImpl implements UserRepository {
    @Autowired
    private UserMapper userMapper;

    /**
     * ユーザーの全てのデータを取得します。
     *
     * @return ユーザーのリスト
     */
    @Override
    public List<User> findAll() {
        return userMapper.selectAll();
    }

    /**
     * 新しいユーザをデータベースに保存します。
     * @param user 保存するユーザーオブジェクト
     */
    @Override
    public void save(User user) {
        userMapper.insertUser(user);
    }

    /**
     * 与えられたIDに一致するユーザーをデータベースから検索し、返します。
     * @param id 検索するユーザーのID
     * @return 検索したユーザーのオブジェクト
     */
    @Override
    public User findById(Long id) {
        return userMapper.selectById(id);
    }

    /**
     * 与えられたユーザーオブジェクトの情報を使って、データベースに保存されている該当のユーザー情報を更新します。
     * @param user 更新するユーザーオブジェクト
     */
    @Override
    public void update(User user) {
        userMapper.updateUser(user);
    }

    /**
     * 与えられたIDに一致するユーザーをデータベースから削除します。
     * @param id 削除するユーザーのID
     */
    @Override
    public void delete(Long id) {
        userMapper.deleteUser(id);
    }
}

サービスクラス

ユーザーエンティティに対するサービスクラスです。

src/main/java/com/example/demo/service/UserService.java

package com.example.demo.service;

import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * UserService クラス。ユーザーの各種サービスを提供します。
 * 各メソッドは UserRepository インターフェースを用いてデータアクセスを行います。
 */
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    /**
     * データベースに登録されている全てのユーザー情報を取得します。
     *
     * @return すべてのユーザー情報のリスト
     */
    public List<User> getAllUsers() {
        return userRepository.findAll();
    }

    /**
     * 新規ユーザーをデータベースに保存します。
     *
     * @param user 新規ユーザーオブジェクト
     * @return 保存されたユーザーオブジェクト
     */
    public User createUser(User user) {
        userRepository.save(user);
        return user;
    }

    /**
     * 指定されたIDのユーザー情報を更新します。
     *
     * @param id 更新するユーザーのID
     * @param name 新しいユーザー名
     * @param phoneNumber 新しい電話番号
     * @return 更新後のユーザーオブジェクト。指定されたIDのユーザーが存在しない場合はnull
     */
    public User updateUser(Long id, String name, String phoneNumber) {
        User user = userRepository.findById(id);
        if (user != null) {
            user.setName(name);
            user.setPhoneNumber(phoneNumber);
            userRepository.update(user);
        }
        return user;
    }

    /**
     * 指定されたIDのユーザー情報を部分的に更新します。
     * 入力オブジェクトに設定されているフィールドのみが更新されます。
     * ユーザー名と電話番号は一度に一つだけ更新可能です。
     * 両方が設定されている場合は例外がスローされます。
     *
     * @param id 部分更新するユーザーのID
     * @param partialUser 部分更新データを含むユーザーオブジェクト
     * @return 更新後のユーザーオブジェクト。指定されたIDのユーザーが存在しない場合はnull
     */
    public User partialUpdateUser(Long id, User partialUser) {
        if (!partialUser.isPartialUpdateValid()) {
            throw new IllegalArgumentException("Only one field (name or phoneNumber) can be updated at a time.");
        }
        User user = userRepository.findById(id);
        if (user != null) {
            if (partialUser.getName() != null) {
                user.setName(partialUser.getName());
            } else if (partialUser.getPhoneNumber() != null) {
                user.setPhoneNumber(partialUser.getPhoneNumber());
            }
            userRepository.update(user);
        }
        return user;
    }

    /**
     * 指定されたIDのユーザー情報をデータベースから削除します。
     *
     * @param id 削除するユーザーのID
     */
    public void deleteUser(Long id) {
        userRepository.delete(id);
    }
}

コントローラークラス

ユーザーエンティティに対するコントローラークラスです。

src/main/java/com/example/demo/controller/UserController.java

package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;

/**
 * UserControllerクラスです。
 * ユーザーに関連した要求を処理し、適切なUserServiceのメソッドを呼び出します。
 */
@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * ユーザーの全リストを取得します。
     * HTTPのGETメソッドを作り、全てのユーザーの情報を提供します。
     *
     * @return ユーザーの全リスト
     */
    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }

    /**
     * ユーザーを作成します。
     * HTTPのPOSTメソッドを作り、新規ユーザーの情報を受け取り、
     * UserService#createUserメソッドを呼び出すことで新規ユーザーを作成します。
     *
     * @param user 新規ユーザーオブジェクト。このオブジェクトはHTTPリクエストのボディから取得されます。
     * @return 新規作成したユーザーオブジェクト
     */
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.createUser(user);
    }

    /**
     * 既存のユーザーを更新します。
     * HTTPのPUTメソッドを作り、指定のIDでユーザーを検索し、そのユーザーの情報を更新します。
     *
     * @param id 更新するユーザーのID。URLパスから取得されます。
     * @param user 更新を行うユーザーオブジェクト。このオブジェクトはHTTPリクエストのボディから取得されます。
     * @return 更新後のユーザーオブジェクト
     */
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.updateUser(id, user.getName(), user.getPhoneNumber());
    }

    /**
     * 既存のユーザーの部分更新(パッチ)を行います。
     * HTTPのPATCHメソッドを作り、指定のIDでユーザーを検索し、そのユーザーの一部情報だけを更新します。
     *
     * @param id 更新するユーザーのID。URLパスから取得されます。
     * @param user 部分更新データを含むユーザーオブジェクト。このオブジェクトはHTTPリクエストのボディから取得されます。
     * @return 更新後のユーザーオブジェクト
     */
    @PatchMapping("/{id}")
    public User partialUpdateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.partialUpdateUser(id, user);
    }

    /**
     * ユーザーを削除します。
     * HTTPのDELETEメソッドを作り、指定のIDでユーザーを検索し、そのユーザーを削除します。
     *
     * @param id 削除するユーザーのID。URLパスから取得されます。
     */
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}

application.properties

Spring Bootアプリケーションの設定ファイルです。ここではデータベース接続情報を定義します。

src/main/resources/application.properties

spring.datasource.url=jdbc:postgresql://db:5432/mydb
spring.datasource.username=user
spring.datasource.password=password
spring.datasource.driver-class-name=org.postgresql.Driver

mybatis.configuration.map-underscore-to-camel-case=true

ステップ3: アプリケーションの実行

以下のコマンドを使用してアプリケーションをビルドし、Dockerコンテナを起動します。

InteliiJ IDEAのTerminalからコマンドラインコンソールを開き、コマンドを実行してください。

アプリケーションのビルド

./gradlew clean
./gradlew build

アプリケーションのソースコード等を更新した場合には、必ずこのコマンドを実行してください。

アプリケーションのビルドログと結果

(base) xxxx@xxxxxx my-springboot-app % ./gradlew build

Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to <https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings> in the Gradle documentation.

BUILD SUCCESSFUL in 783ms
4 actionable tasks: 4 executed

コンテナイメージをビルドして、Dockerコンテナを起動します。

docker compose up --build -d

Dockerコンテナの実行ログ

(base) xxxx@xxxxxx my-springboot-app % docker compose up --build -d
[+] Building 0.9s (8/8) FINISHED                                                                                             docker:desktop-linux
 => [app internal] load build definition from Dockerfile                0.0s
 => => transferring dockerfile: 125B                                    0.0s
 => [app internal] load metadata for docker.io/library/openjdk:21-slim  0.8s
 => [app internal] load .dockerignore                                   0.0s
 => => transferring context: 2B                                         0.0s
 => [app internal] load build context                                   0.0s
 => => transferring context: 24.44MB                                    0.0s
 => [app 1/2] FROM docker.io/library/openjdk:21-slim@sha256:70720       0.0s
 => CACHED [app 2/2] COPY build/libs/*.jar app.jar                      0.0s
 => [app] exporting to image                                            0.0s
 => => exporting layers                                                 0.0s
 => => writing image sha256:8e33ad7ffdb8f722938dae373a932d4eccf2c3c71   0.0s
 => => naming to docker.io/library/my-springboot-app-app                0.0s
 => [app] resolving provenance for metadata file                        0.0s
[+] Running 3/3
 ✔ Network my-springboot-app_default  Created                           0.0s 
 ✔ Container my-springboot-app-db-1   Started                           0.1s 
 ✔ Container my-springboot-app-app-1  Started                           0.2s 
 (base) xxxx@xxxxxx my-springboot-app % 

これで、Spring BootアプリケーションがPostgreSQLデータベースと連携して動作する環境が整いました。APIエンドポイントはCurlコマンドでアクセス可能です。

ユーザー情報の作成から編集までの一通りの操作例を以下に示します。

全てのユーザー情報を取得し、データが無いことを確認する。

ユーザー取得コマンド

curl -X GET <http://localhost:8080/users>

実行結果

(base) xxxx@xxxxxx my-springboot-app % curl -X GET <http://localhost:8080/users>
[]%

新しいユーザーを作成します。

ユーザー作成コマンド

curl -X POST <http://localhost:8080/users> -H "Content-Type: application/json" -d '{"name": "John Doe", "phoneNumber": "123-456-7890"}' 

作成後、全ユーザー取得コマンドを実行します。

curl -X GET <http://localhost:8080/users>

先ほど作成した、ユーザーが取得できます。

(base) xxxx@xxxxxx my-springboot-app % curl -X GET <http://localhost:8080/users> 
[{"id":1,"name":"John Doe"}]%

ユーザー更新コマンドです。すべての項目を変更します。

curl -X PUT <http://localhost:8080/users/1> -H "Content-Type: application/json" -d '{"name": "Updated Name", "phoneNumber": "123-456-7890"}'

ユーザー削除コマンドです。idが1のユーザーが削除されます。

curl -X DELETE <http://localhost:8080/users/1>

README.md

プロジェクトの概要やセットアップ手順を記載するためのファイルです。必要に応じて内容を追加してください。

# My Spring Boot Application

This is a simple Spring Boot application with PostgreSQL and MyBatis.

## Requirements

- Docker
- Docker Compose
- JDK 21
- Gradle

## Setup

1. Build the application:

    ```bash
    ./gradlew build
    ```

2. Start the application with Docker Compose:

    ```bash
    docker compose up --build -d
    ```

3. Access the application at `http://localhost:8080`.

## API Endpoints

- `GET /users`: Get all users
- `POST /users`: Create a new user

お試し生成AI

プロンプト追加

IntelliJ IDEAでは、Community版でもUltimate版でもAI Assistantを利用出来ます。

デフォルトでいくつかプロンプトが準備されていますが、今回は「ソースコードのコメント生成」のプロンプトを追加して、そのプロンプトを使ってコメントを付けていきます。

まずは、プロンプトの追加です。

コードエディタで右クリックメニューを開くと、「AI Actions」のメニューがあります。

このメニューのサブメニューから「Add Your Prompts…」を選択します。

AI Assistantでプロンプトを追加します

すると、「Toold > AI Assistant > Prompt Library」が開きます。

この真ん中にある「+」ボタンをクリックすると、右にプロンプトが入力できるエリアが表示されるので、以下の様にプロンプトを入力し、「Applyボタン」を押し、追加します。

追加するプロンプトの編集画面です

各ソースコードのコメント生成

追加されたプロンプトを実行します。コードエディタを開いて、コメントを生成する範囲を選択します。(今回は、ファイルの内容全部選択します)

選択した状態で、右クリックメニューから「AI Actions」→ 「コメント生成」を選択します。

すると、以下の様にAI Assistantがコメントを生成します。←矢印のボタンをクリックすると、元のコードにコメントが反映されます、(便利ですね!!)

生成AIで作成したコメントをソースコードに反映します

実行結果

実行結果は、本記事のソースコードをご覧下さい。こちらのコメントは、すべてAI Assistantを使って生成したものになります。

考察

コメントは、実用上精度がかなり高く生成されていると感じました。これを使えば、かなり効率は良くなると感じました。ChatGPTで生成してからコピペするよりも、直接コードにマージしてくれるので、ツールの切替えによる思考の中断なども防止できると思います。

また、所々誤字や誤った内容が出力されている場合がありますので、生成された内容をしっかりと確認する必要はあります


まとめ

このプロジェクトを通じて、Spring Bootを使ったWebAPI開発の環境構築から開発、実行までの手順を紹介しました。主なポイントを、以下に示します。

  • プロジェクト※構成管理: Spring Bootを使ったディレクトリ構造やファイルの役割を理解し、効率的にプロジェクトを管理する。※アプリを開発するためのファイルやフォルダの集まり。
  • ビルドと依存関係管理: Gradleを使ってプロジェクトのビルドと依存関係を管理する。
  • コンテナ技術: DockerとDocker Composeを使って、アプリをコンテナ化し、開発および本番環境の差異をなくす。
  • API開発: Spring Bootを使って、RESTful APIを作成し、他のシステムやクライアントとデータをやり取りする。
  • データベース操作: MyBatisを使って、安全かつ効率的にデータベースと連携する。
  • ドメイン駆動設計: ソフトウェア開発の効率や品質を向上させる。
  • Jetbrains社が提供する生成AIの機能をつかってコーディングの効率化出来る様にする
    • AI Assistant コメントの追加、アプリケーションの提案(サーバーサイドAI)

最後に、この記事がJavaの学習やSpring Boot開発に役立つことを願っています。

これからもSpring BootとInteliiJ IDEA, Dockerコンテナを活用して、より高度なアプリケーション開発に挑戦してみてください!

SNSでもご購読できます。

コメントを残す

*


reCaptcha の認証期間が終了しました。ページを再読み込みしてください。