Spring BootでMyBatisを使ってみよう!(1)

mybatis プログラミング

この記事ではSpring Boot環境へのMyBatisの導入方法とMyBatisの機能について、紹介して行きたいと思います。

MyBatisとは?

mybatis

MyBatisはJavaの永続化フレームワークで、DBとJavaオブジェクトを紐づける事ができ、
ORマッパー(Object/RDB Mapper)と呼ばれるます。
MyBatisではXMLやアノテーションを使って設定を行うことができます。
MyBatisは直接SQLを記述する事が可能で、複雑なSQLやパフォーマンスチューニングが必要なケースで威力を発揮します。

以前はiBATISというApacheプロジェクトで開発されていましたが、2010年からMyBatisというプロジェクト名に変更され開発が続いています。

公式でSpring Boot環境で利用するためのstarterライブラリが提供されているため、非常に簡単に導入することが可能です。

公式サイト
https://mybatis.org/

環境

  • Spring Boot: 2.5.4
  • Java 11
  • gradle
  • DB: H2(インメモリDB)

MyBatisの導入

build.gradleにMyBatisのスターターライブラリとDBとして使用するH2のライブラリを追加します。

dependencies {
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0'
    runtimeOnly 'com.h2database:h2:1.4.200'
    testImplementation 'org.springframework.boot:spring-boot-starter-test:2.5.4'
}

次にSpring Bootの設定ファイル(application.yml)にh2への接続設定を行います。

spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driverClassName: org.h2.Driver
    username: sa
    password:

上記の設定ではtestdbという名前のインメモリDBを使用するようにしています。

ここまででMyBatisを利用する準備が整いました。

データベーステーブルの準備

この記事ではタスクを管理するテーブルを題材に説明を進めます。
よくあるTODO管理を思い浮かべるとわかりやすいと思います。

タスクテーブル(task)

タスクを管理するテーブル

カラム名名称タイプ属性
idIDintKEY NOT NULL 自動採番
task_descタスク名varchar(1000)NOT NULL
done完了booleanNOT NULL デフォルト値: false
created_at作成日時timestampNOT NULL
updated_at更新日時timestamp
タスクタグテーブル(task_tag)

タスクに紐づけるタグを管理するテーブル。
1件のタスクに対して複数件紐づけることができます。

カラム名名称タイプ属性
idIDintKEY NOT NULL 自動採番
task_idタスクIDintNOT NULL FK(task:id)
tag_nameタグ名varchar(100)NOT NULL
order_seq表示順intNOT NULL
created_at作成日時timestampNOT NULL
updated_at更新日時timestamp

データの初期化

Spring BootにはDBを初期化する機能があり、クラスパス上(src/main/resources直下)に決められたsqlファイルを配置するだけでアプリケーションの実行時に自動的に初期化を実行してくれます。

  • schema.sql: DDL(テーブル作成文など)を記述
  • data.sql: insert文などのデータ操作文を記述
-- タスク
create table task
(
    id         int           not null auto_increment,
    task_desc  varchar(1000) not null,
    done       boolean       not null default false,
    created_at timestamp     not null,
    updated_at timestamp,
    primary key (id)
);

-- タスクタグ
create table task_tag
(
    id         int          not null auto_increment,
    task_id    int          not null,
    tag_name   varchar(100) not null,
    order_seq  int          not null,
    created_at timestamp    not null,
    updated_at timestamp,
    primary key (id),
    foreign key (task_id) references task (id)
);
insert into task values (1, '買い物に行く', false, '2020-12-20 10:20:10');
insert into task values (2, 'スケジュールを作る', false, '2021-03-01 18:05:42');
insert into task values (3, '会議を招集する', false, '2021-05-23 13:05:42');

insert into task_tag values(1, 1, 'プライベート', 1, '2020-12-20 10:20:10');
insert into task_tag values(2, 1, '外出', 2, '2020-12-20 10:20:10');

insert into task_tag values(3, 2, '仕事', 1, '2021-03-01 18:05:42');
insert into task_tag values(4, 2, '急ぎ', 2, '2021-03-01 18:05:42');

insert into task_tag values(5, 3, '仕事', 1, '2021-05-23 13:05:42');
insert into task_tag values(6, 3, '定例', 2, '2021-05-23 13:05:42');

MyBatisを使ったDBアクセスの実装

MyBatisを使ってDBへアクセスするために必要なもの

エンティティクラステーブル・ビューと値をやり取りするためのクラス
マッパーインターフェースDBテーブルへの動作を定義するためのインターフェース
マッパーXML実行するSQLやDBテーブルとエンティティクラスへのマッピングなどを記述するXMLファイル。
XMLを使用せずにマッパーインターフェースにアノテーションで定義することも可能。

エンティティクラスの実装

タスクテーブル、テスクタブテーブルをマッピングするためのエンティティクラスを実装します。
エンティティクラスは特定のインターフェースの実装やアノテーションの付与などは必要ありません。
値を保持する為のフィールドにはgetter, setterを定義する必要があります。
この記事ではgetter, setterの自動生成のためにLombokというライブラリを利用しています。(※1)

package com.example.springbootmybatis.entity;

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

@Data // ※1
public class Task {
    private int id;
    private String taskDesc;
    private boolean done;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}
package com.example.springbootmybatis.entity;

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

@Data
public class TaskTag {
    private int id;
    private int taskId;
    private String tagName;
    private int orderSeq;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

マッパーインターフェースの実装

DBテーブルへの動作を定義するためにマッパーインターフェースを実装します。
まずはタスクテーブルを操作するためのTaskMapperを作成し、全てのタスクを取得するfindAllメソッドを定義します。

package com.example.springbootmybatis.mapper;

import com.example.springbootmybatis.entity.Task;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface TaskMapper {
    public List<Task> findAll();
}

インターフェースに@Mapperアノテーションを付与することで自動的にSpringのBeanとして登録されます。

マッパーXMLの実装

先ほど実装したTaskMapperの対となるマッパーXMLを実装します。
マッパーXMLはsrc/main/resources以下に配置し、マッパーインターフェースのパッケージ配置と同じ配置にインターフェース名と同名になるように配置します。
TaskMapperはcom.example.springbootmybatis.mapperパッケージに配置されていますので、マッパーXMLはsrc/main/resources/com/example/springbootmybatis/mapper/TaskMapper.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.springbootmybatis.mapper.TaskMapper">

    <resultMap id="Base_Result_Map" type="com.example.springbootmybatis.entity.Task">
        <id column="id" property="id" jdbcType="INTEGER"/>
        <result column="task_desc" property="taskDesc" jdbcType="VARCHAR"/>
        <result column="done" property="done" jdbcType="BOOLEAN"/>
        <result column="created_at" property="createdAt" jdbcType="TIMESTAMP"/>
        <result column="updated_at" property="updatedAt" jdbcType="TIMESTAMP"/>
    </resultMap>

    <select id="selectAll" resultMap="Base_Result_Map">
        select
        id, task_desc, done, created_at, updated_at
        from task
        order by id
    </select>
</mapper>
resultMapの定義

resultMapタグを定義し、エンティティクラスとテーブルカラムの紐づけを行います。
id属性にこのresultMapを特定するためのIDを指定し、type属性に紐づけるエンティティクラスを指定します。

次にresultMapタグの子要素にテーブルカラムの定義を行います。
キーカラムにはidタグを使い、キー以外のカラムにはresultタグを使用し、column属性にテーブルカラム名を指定し、property属性にエンティティクラスのフィールド名をしています。
jdbcType属性にはテーブルカラムの型を指定します。jdbcType属性の一覧はこちら

selectの定義

selectタグを定義し、 TaskMapperのselectAllメソッドと紐づけるSQLを定義します。
id属性には紐づけるメソッド名を指定する必要がありますので、ここではselectAllを設定します。
resultMap属性には結果として返却する定義情報を指定する必要があり、先ほど定義したresultMapのidを指定します。最後にselectタグのテキストには実際に実行するSQLを記述します。

TaskMapperの実行

ここまででMyBatisを実行するための準備が整いましたので、TaskMapperを実行して全てのタスクを取得してみましょう。

package com.example.springbootmybatis.service;

import com.example.springbootmybatis.entity.Task;
import com.example.springbootmybatis.mapper.TaskMapper;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class TaskServiceImpl implements TaskService {
    private final TaskMapper taskMapper;
    
    // コンストラクタインジェクション
    public TaskServiceImpl(TaskMapper taskMapper) {
        this.taskMapper = taskMapper;
    }
    
    @Override
    public List<Task> findAll() {
        return taskMapper.findAll();
    }
}

TaskMapperはSpringのBeanとして登録されていますので、他のBeanにDIする事が可能です。上のサンプルではコンストラクターインジェクションでTaskServiceImplにインスタンスを注入しています。

まとめ

この記事ではSpring Boot環境でMyBatisを使えるようにする設定を説明しました。
次回はMyBatisの機能を中心に説明していきたいと思います。

最後までお読みいただき、ありがとうございました。

コメント

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