作記録

記憶代わり

MyBatis Spring-Boot-Starter(MyBatis Integration with Spring Boot)について

1. 本記事で利用した技術のVersion

  • openjdk 11.0.9
  • mybatis-spring-boot-starter 2.2.0
  • PostgreSQL 13.1

2. MyBatis Spring Boot Starter GitHub

github.com

3. 公式 MyBatis document

MyBatis

https://mybatis.org/mybatis-3/ja/index.html

MyBatis-Spring

https://mybatis.org/spring/ja/index.html

MyBatis Spring-boot-starter

http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure

4. この記事の参考用GitHub

https://github.com/sakuoden/mybatis-sample

5. MyBatis Spring-Boot-Starter概要

MyBatis単体でMapperを使うまでの手順

本来MyBatisを利用してMapperを利用するまでの手順は下記の通りである。

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
}
  1. 設定xmlファイルを読み込んで SqlSessionFactory を生成する。
  2. SqlSessionFactory により SqlSession を開く。
  3. SqlSession のgetMapperメソッドでMapperを取得する。
  4. Mapperで作成したメソッドを利用する。

参考記事

MyBatis Spring単体でMapperを使うまでの手順

@Configuration
public class MyBatisConfig {
  @Bean
  public DataSource dataSource() {
      return DataSourceBuilder.create().type(HikariDataSource.class).build();
  }

  @Bean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
    factoryBean.setDataSource(dataSource);
    return factoryBean.getObject();
  }
}
try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
}
  1. DataSourceのBeanを登録する。
  2. SqlSessionFactoryのBeanを登録する。
  3. 利用したい箇所で登録したSqlSessionFacotryのBeanをInjectionする。

MyBatis Spring-Boot-Starterは...?

上記のようなDataSourceのBean登録やSqlSessionFactoryのBean登録などをSpring Bootが自動で行なってくれる。

6. 必要なライブラリのインストール

dependencies {
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:?.?.?
    runtimeOnly 'org.postgresql:postgresql'
}

org.mybatis.spring.boot:mybatis-spring-boot-starter

上記の?.?.? は自身でversionを記述する。 利用できるversionはMaven Repositoryを参照すると良い。

本記事ではMyBatis Spring-Boot-Starter 2.2.0を利用するため下記のような記述になる。

dependencies {
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0
    runtimeOnly 'org.postgresql:postgresql'
}

org.postgresql:postgresql

このライブラリは、PostgreSQLに接続するためのJDBCである。

参考実装

https://github.com/sakuoden/mybatis-sample/blob/main/build.gradlehttps://github.com/sakuoden/mybatis-sample/blob/main/build.gradle#L22-L23

7. build対象の設定

gradleのjavaプラグインのデフォルトだと、src/main/java 配下に配置するxmlファイルをbuildしない。
gradleのjavaプラグインは、src/main/javajavaファイルのみを読み取るためである。

見通しを良くする目的でHogeMapper.javaファイルと同じディレクトリにHogeMapper.xmlファイルを配置すると、上記の理由からHogeMapper.xmlはbuildされない。
結果として、HogeMapper.javaファイルと同じディレクトリにHogeMapper.xmlファイルを配置するとHogeMapper.xmlファイルは存在するのにbuild先には存在しないため下記のエラーが出る。

Invalid bound statement (not found): jtn.sample.user.UserMapper.insert
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): jtn.sample.user.UserMapper.insert

これの解決策としてsrc/main/java配下のxmlファイルをbuild先のresourcesディレクトリにbuildするようにする。
これにより、MyBatisがxmlファイルを読み込めるようになり、後述するMapper.xmlファイルを読み込めるようになる。
このbuild.gradleの設定が下記である。

sourceSets {
    main {
        resources.srcDirs = ['src/main/java', 'src/main/resources']
    }
}

参考実装

https://github.com/sakuoden/mybatis-sample/blob/main/build.gradle#L11-L15

参考記事

8. DataSourceの設定

application.properties

application.propertiesにDataSourceのオブジェクトの値を記述する。
基本的には下記の4点を設定すれば良い。
- driver-class-name - url - username - password

参考実装

参考記事

Spring データアクセス リファレンス

9. Mapper Interfaceの作成

@Mapper
public interface DentalClinicMapper {
    void insert(@Param("dentalClinic") DentalClinic dentalClinic);

    Optional<DentalClinic> find(@Param("clinicNumber") ClinicNumber clinicNumber);
}

参考実装

10. MapperXMLの作成

MapperXMLの基本テンプレート

<?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="●●●.●●●Mapper">
  
</mapper>

下記のmapperタグ内にMapperの構文を書く。
●●●.●●●Mapperには、上記の9で作成したMapper interfaceの完全修飾クラス名を記載する。

参考記事

11. Selectしたデータベースの情報をJavaオブジェクトのインスタンスにMappingする

クラス設計

public class DentalClinic {
    ClinicNumber clinicNumber;
    ClinicName clinicName;
   
    DentalClinic() {}
}
public class ClinicNumber {
    String value;

    ClinicNumber() {}
}
public class ClinicName {
    String value;

    public ClinicName() {}

    public ClinicName(String clinicName) {
        this.value = clinicName;
    }
}

上記のDentalClinicクラスの通り、MyBatisでMappingするオブジェクトのクラスは、デフォルトコンストラクタを持つ必要がある。
ClinicNumber, ClinicNameクラスの通り、そのオブジェクトのクラスが持つオブジェクトのクラスも同様である。

ResultMapを利用しない書き方

    <select id="find" resultType="jtn.sample.dentalclinic.DentalClinic">
        SELECT
            dental_clinics.clinic_number AS "clinicNumber.value",
            dental_clinics.clinic_name AS "clinicName.value"
        FROM 
            dental_clinics
        WHERE
            dental_clinics.clinic_number = #{clinicNumber.value}
    </select>

idには、Mapper Interfaceで定義したメソッド名を記述する。
resultTypeには、マッピングする対象のオブジェクトの完全修飾クラス名を定義する。
SELECTのカラム名に、マッピングする対象のオブジェクトのfieldの変数名を AS を利用して名づける。fieldの型がプリミティブじゃない場合、プリミティブな型にたどり着くまで . でチェーンする。

ResultMapを利用する書き方

    <resultMap id="DentalClinic" type="jtn.sample.dentalclinic.DentalClinic">
        <id property="clinicNumber.value" column="clinic_number"/>
        <result property="clinicName.value" column="clinic_name"/>
    </resultMap>

    <select id="find" resultMap="DentalClinic">
         SELECT
            dental_clinics.clinic_number,
            dental_clinics.clinic_name
        FROM 
            dental_clinics
        WHERE
            dental_clinics.clinic_number = #{clinicNumber.value}
    </select>

resultMapのidには、ResultMapを識別するIdentityを記述する。
resultMapのTypeには、マッピングする対象のオブジェクトの完全修飾クラス名を定義する。
resultMapタグの配下のidタグは、マッピングする対象のオブジェクトのIdentityに該当するものに対して使用する。それ以外にはresultタグを使用する。
, それぞれのタグのpropertyにマッピングする対象のオブジェクトのfieldの変数名を記述する。fieldの型がプリミティブじゃない場合、プリミティブな型にたどり着くまで . でチェーンする。 , それぞれのタグのcolmnにマッピングする値の元となるカラム名を指定する。

参考記事