プログラミング

MySQLとSpringBootの連携方法【MyBatis/Maven/VSCode】

この記事は約18分で読めます。

こんにちは。じゅんです。

今回は、開発(ローカル)環境でMySQLとSpringBootを連携させる方法についてまとめます。

Spring Bootプロジェクトの作成は以下の記事で詳しく解説しています。


また、これまでの作業内容については以下の記事にまとめています。

MySQL側のセットアップ

まず、HomebrewでMySQLをインストールします。

brewがインストールされていない場合は公式HPを参考にHomeBrewをインストールします。

Homebrew
The Missing Package Manager for macOS (or Linux).
Zsh
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

実行結果の最後にHomebrewをPATHに追加するためのコマンドが記述されるので実行します。

versionが確認できればインストール完了です。

Zsh
% brew --version

Homebrew 4.3.7
Zsh
brew install mysql

インストールが完了したらMySQLを起動します。

Zsh
brew services start mysql

MySQLにログインします。
以下のコードはrootユーザーでログインするコマンドです。

Zsh
mysql -u root

データベースを作成します。

SQL
CREATE DATABASE simple_memo;

以下のコマンドで作成できたか確認できます。

SQL
mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| simple_memo        |
+--------------------+
1 rows in set (0.01 sec)

テーブルを作成するデータベースを選択します。

SQL
USE simple_memo;

以下のコマンドでデータベースが作成されたか確認できます。

SQL
mysql> SELECT DATABASE();
+-------------+
| DATABASE()  |
+-------------+
| simple_memo |
+-------------+
1 row in set (0.00 sec)

テーブルを作成します。作成済みのSpringBootに合わせます。

SQL
CREATE TABLE memos (
    id INT AUTO_INCREMENT PRIMARY KEY,  -- メモのID。自動インクリメントされ、一意の値が設定される
    content TEXT,                       -- メモの内容。
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,  -- 作成日時。デフォルトで現在のタイムスタンプが設定される
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP  -- 更新日時。デフォルトで現在のタイムスタンプが設定され、更新時に自動的に現在のタイムスタンプに更新される
);

以下のコマンドでテーブルが作成できたか確認できます。

SQL
mysql> SHOW TABLES;
+-----------------------+
| Tables_in_simple_memo |
+-----------------------+
| memos                 |
+-----------------------+
1 row in set (0.01 sec)

なお、テーブルの詳細は以下のコマンドで確認できます。

SQL
mysql> DESCRIBE memos;
+------------+-----------+------+-----+-------------------+-----------------------------------------------+
| Field      | Type      | Null | Key | Default           | Extra                                         |
+------------+-----------+------+-----+-------------------+-----------------------------------------------+
| id         | int       | NO   | PRI | NULL              | auto_increment                                |
| content    | text      | YES  |     | NULL              |                                               |
| created_at | timestamp | YES  |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED                             |
| updated_at | timestamp | YES  |     | CURRENT_TIMESTAMP | DEFAULT_GENERATED on update CURRENT_TIMESTAMP |
+------------+-----------+------+-----+-------------------+-----------------------------------------------+

Spring Boot側のセットアップ

Spring BootプロジェクトにMySQLとMyBatisを設定します。

依存関係の追加

MySQLを扱うためにライブラリやフレームワークを使用します。

使用するライブラリやフレームワークは pom.xml に追記します。

pom.xml
		<!-- MySQLコネクタ -->
		<dependency>
				<groupId>mysql</groupId>
				<artifactId>mysql-connector-java</artifactId>
				<version>8.0.27</version>
				<scope>runtime</scope>
		</dependency>

		<!-- MyBatis Starter -->
		<dependency>
				<groupId>org.mybatis.spring.boot</groupId>
				<artifactId>mybatis-spring-boot-starter</artifactId>
				<version>3.0.3</version>
		</dependency>
		
		<!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
SpringBoot起動時に factoryBeanObjectType エラーが発生しました。
調べたところMyBatisのバージョンが合っていない可能性があるとのことなのでv3.0.3にしたところ解消しました。

MySQLへの接続情報の設定

MySQLに接続するための接続情報を application.properties に定義します。

src/main/resources/application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/simple_memo?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.url=jdbc:mysql://localhost:3306/simple_memo?useSSL=false&serverTimezone=UTC

データソースのURLを示します。ここでは、MySQLサーバーがローカルホスト(自分のコンピュータ)の3306ポート(デフォルト)で実行されており、データベース名はsimple_memoです。useSSL=falseはSSLを使用しないことを指定し、serverTimezone=UTCはタイムゾーンをUTCに設定します。

spring.datasource.username=root

データベースに接続するためのユーザー名を指定します。ここでは、MySQLのデフォルトの管理者ユーザーである”root”を使用しています。

マッパーファイルは、データベースとアプリケーションの間で行われるSQLクエリとJavaのメソッドをマッピング(対応づけ)するために作成されます。

永続化層の作成

MemoMapperインターフェースを作成します。

MemoMapperインターフェースは永続化層と呼ばれ、データベースと最も近い層のファイルで、SQLクエリを定義します。

src/main/java/com/example/simplememo/mapper/MemoMapper.java
package com.example.simplememo.mapper;

import com.example.simplememo.model.Memo;
import org.apache.ibatis.annotations.*;

import java.util.List;

@Mapper
public interface MemoMapper {
    @Select("SELECT * FROM memos")
    List<Memo> findAll();

    @Select("SELECT * FROM memos WHERE id = #{id}")
    Memo findById(int id);

    @Insert("INSERT INTO memos(content, created_at, updated_at) VALUES(#{content}, #{createdAt}, #{updatedAt})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insert(Memo memo);

    @Update("UPDATE memos SET content=#{content}, updated_at=#{updatedAt} WHERE id=#{id}")
    void update(Memo memo);

    @Delete("DELETE FROM memos WHERE id=#{id}")
    void delete(int id);
}

また、このMapperを使うためには @MapperScan というアノテーションを@SpringBootApplicationが付いたクラスに追加してMyBatisMapperをスキャンする必要があります。

src/main/java/com/example/simplememo/SimpleMemoApplication.java
package com.example.simplememo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.simplememo.mapper")
public class SimpleMemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(SimpleMemoApplication.class, args);
	}

}

ビジネスロジック層の作成

Serviceファイルはビジネスロジック層と呼ばれ、永続化層とプレゼンテーション層の間に位置する層です。データの加工などを行います。

src/main/java/com/example/simplememo/service/MemoService.java
package com.example.simplememo.service;

import com.example.simplememo.mapper.MemoMapper;
import com.example.simplememo.model.Memo;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class MemoService {
    private final MemoMapper memoMapper;

    public MemoService(MemoMapper memoMapper) {
        this.memoMapper = memoMapper;
    }

    public List<Memo> findAll() {
        return memoMapper.findAll();
    }

    public Memo findById(int id) {
        return memoMapper.findById(id);
    }

    public void insert(Memo memo) {
        memoMapper.insert(memo);
    }

    public void update(Memo memo) {
        memoMapper.update(memo);
    }

    public void delete(int id) {
        memoMapper.delete(id);
    }
}
se

プレゼンテーション層の修正

Controllerファイルはプレゼンテーション層と呼ばれ、ユーザーからのリクエストを受け取り、適切なサービスを呼び出します。

src/main/java/com/example/simplememo/controller/SimpleMemoController.java
package com.example.simplememo.controller;

import com.example.simplememo.model.Memo;
import com.example.simplememo.service.MemoService;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.List;

@RestController
@RequestMapping("/api/memos")
public class SimpleMemoController {
    private final MemoService memoService;

    public SimpleMemoController(MemoService memoService) {
        this.memoService = memoService;
    }

    // 全てのMemoレコードを取得するエンドポイント
    @GetMapping
    public List<Memo> getAllMemos() {
        return memoService.findAll();
    }

    // 新しいMemoレコードを作成するエンドポイント
    @PostMapping
    public int createMemo(@RequestBody String content) {
        // リクエストの内容から新しいMemoオブジェクトを作成し、Serviceに挿入を依頼
        Memo newMemo = new Memo(0, content, LocalDateTime.now(), LocalDateTime.now());
        memoService.insert(newMemo);
        // 作成したMemoのIDを返す
        return newMemo.getId();
    }

    // 指定されたIDのMemoレコードを更新するエンドポイント
    @PutMapping("/{id}")
    public void updateMemo(@PathVariable int id, @RequestBody String content) {
        // 指定されたIDのMemoレコードをServiceから取得し、内容を更新
        Memo memoToUpdate = memoService.findById(id);
        if (memoToUpdate != null) {
            memoToUpdate.setContent(content);
            memoToUpdate.setUpdatedAt(LocalDateTime.now());
            memoService.update(memoToUpdate);
        }
    }

    // 指定されたIDのMemoレコードを削除するエンドポイント
    @DeleteMapping("/{id}")
    public void deleteMemo(@PathVariable int id) {
        // 指定されたIDのMemoレコードをServiceから削除
        memoService.delete(id);
    }
}

クラスファイルの修正

データベースのデータを格納するためのクラスファイルを修正します。

Lombokを使ってコードを簡潔にします。
Lombokの@Dataを使うことで、getterとsetterだけでなく引数なしコンストラクタも省略することができます。(引数ありコンストラクタは定義する必要があります。)

src/main/java/com/example/simplememo/model/Memo.java
package com.example.simplememo.model;

import lombok.Data;
import java.time.LocalDateTime;

@Data
public class Memo {
    private int id;
    private String content;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;

    public Memo(int id, String content, LocalDateTime createdAt, LocalDateTime updatedAt) {
        this.id = id;
        this.content = content;
        this.createdAt = createdAt;
        this.updatedAt = updatedAt;
    }
}

以下のコマンドで実行します。

Zsh
mvn spring-boot:run

動作確認

SpringBootの動作確認をcurlコマンドで行います。
詳細については以下の記事を参考にしてください。

Zsh
# メモを作成
$ curl -s -X POST -H "Content-Type: application/json" -d 'サンプル0' http://localhost:8080/api/memos
1

# メモ一覧を表示
$ curl -s -X GET http://localhost:8080/api/memos | jq
[
  {
    "id": 1,
    "content": "サンプル0",
    "createdAt": "2024-06-07T07:46:36",
    "updatedAt": "2024-06-07T07:46:36"
  }
]

MySQLが正常に動作しているかをターミナルから確認します。

Zsh
$ mysql -u root
# mysqlに入る
mysql> USE simple_memo;
Database changed
mysql> select * from memos;
+----+---------------+---------------------+---------------------+
| id | content       | created_at          | updated_at          |
+----+---------------+---------------------+---------------------+
|  1 | サンプル0     | 2024-06-07 07:46:36 | 2024-06-07 07:46:36 |
+----+---------------+---------------------+---------------------+
1 row in set (0.00 sec)

MySQLに書き込みできていることが分かりました。

まとめ

以上が開発環境でSpringBootとMySQLを連携させる方法でした。

MyBatisのバージョンが合っていなかったところに苦戦しましたが、概ねスムーズに進めたと思います。

参考文献

SpringBoot + Mybatis 起動時にエラー - Qiita
環境Windows10Eclipse 4.8SpringBoot 1.5.3Maven 3.5.4Mybatis3Eclipseにて 実行→SpringBootアプリケーション を実施し…
SpringBoot Mybatis 起動時に factoryBeanObjectType エラー - Qiita
起動時に以下のエラーが発生java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObje…

コメント