Spring + MyBatisの使い方を解説【JPAとの比較+α】

Spring + MyBatisの使い方を解説【JPAとの比較+α】

SpringBoot + MyBatisを実際に使いたい人「SpringでMyBatisの使い方、MyBatisとJPAの違い、Spring MyBatisの求人ってどのくらいあるのか知りたいです。よろしくおねがいします。」


そんな方向けになります。

本記事ではSpring + MyBatisを実際に使えるようになることを目的としています。


構成は以下です。

それでは、順に見ていきましょう。

著者情報

ちなみにですが、私は5年以上IT系エンジニアとして働いており、主にJavaを主戦場にしています。Webアプリケーションと業務系のアプリケーションの経験を持つごく普通のエンジニアです。
また、実際にSpringを使ったアプリケーション開発経験があり、JPAとMyBatisを使用したことがあります。

Spring + MyBatisの使い方を解説

使い方の手順は以下です。

  1. MyBatisのライブラリを取得(Maven)
  2. Javaコンフィグファイルの設定
  3. Mybatisの設定ファイル作成
  4. Mapperインターフェース作成
  5. マッピングファイル作成
  6. Mapperオブジェクト呼び出し



順に説明します。

【手順1】MyBatisのライブラリを取得(Maven)

Mavenプロジェクトの依存関係に以下を追加します。

(省略)
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>2.0.5</version>
</dependency>
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.5.5</version>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
(省略)

※バージョンは本記事作成時の最新のバージョンです。

今回はSpringBootを使用します。

【手順2】Javaコンフィグファイルの設定

DIコンテナにMapperオブジェクトを登録するための設定をします。

package com.example.spring_react.springreactapp;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import 
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;

/**
 * @MapperScanアノテーションを付与することで、
 * value属性に指定したパッケージ以下のMapperインターフェースをMyBatisが検知して、
 * MapperオブジェクトをDIコンテナに登録する
 */
@SpringBootApplication
@MapperScan("com.example.spring_react.springreactapp.mapper") 
public class DemoApplication {

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

	// MyBatisの設定
	@Bean
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
		final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(dataSource);
		// Mybatisの設定ファイルの読み込み
		sessionFactory.setConfigLocation(new ClassPathResource("/mybatis-config.xml"));
	
		return sessionFactory.getObject();
	}

}

【手順3】Mybatisの設定ファイル作成

MyBatis自体の設定を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>
    <settings>
        <!-- 検索結果のフェッチサイズを制御するためのドライバヒントを設定します。 
        このパラメータ値はクエリ毎の設定で上書きできます。 -->
        <setting name="jdbcTypeForNull" value="NULL"/>
        <!-- 引数の JDBC タイプが未指定の場合、null 値に対して割り当てられる JDBC タイプを設定します。
        ドライバによっては列に対する JDBC タイプの指定が必須な場合もありますが、
        NULL, VARCHAR, OTHER などの汎用の型を指定すれば動作するものもあります。 -->
        <setting name="defaultFetchSize" value="100"/>
    </settings>
    <!-- Java タイプに対する短縮名です。 
    XML の中でクラスを指定する際、完全修飾名の記述を最小限で済ませることができます。 -->
    <typeAliases>
        <package name="com.example.spring_react.springreactapp.model"/>
    </typeAliases>
</configuration>



他にも色々設定ができます。詳細はこちらを参考にしてください。

また、こちらのファイルはデプロイ後にクラスパス直下にあるようにしたいので、「src/resouce」ディレクトリ直下に今回は置きます。

【手順4】Mapperインターフェース作成

SQLと紐付けるJavaのオブジェクトのインターフェースを作成します。
(後述してますが、このインターフェースのメソッドをロジック側では呼び出します)

package com.example.spring_react.springreactapp.mapper;

import java.util.List;

import com.example.spring_react.springreactapp.model.User;

/**
 * メソッド名とマッピングファイルのIDが一致するSQLが実行される
 */
public interface UserMapper {

    /**
     * 全ユーザを検索
     */
    List<User> findAll();

    /**
     * Emailカラムで検索するクエリ
     */
    User findByEmail(String email);

    /**
     * ユーザ登録
     */
    void save(User user);
}



ちなみにですが、UserクラスはUserテーブルに紐づくクラスです。
Userクラスのメンバ変数がUserテーブルの各カラムに対応します。

一応、Userクラスも記載しときます。

package com.example.spring_react.springreactapp.model;

import lombok.Data;

@Data
public class User {

    private int id;

    private String name;
    
    private String email;
    
    private String password;
    
    private int age;
    
    private int gender;
    
    private Boolean enable;
    
    private int role;
}

【手順5】マッピングファイル作成

Mapperインターフェースに対応するマッピングファイルを作成します。

<?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">
<!-- namespaceには対応するMapperインターフェースの完全修飾クラス名を指定 -->
<mapper namespace="com.example.spring_react.springreactapp.mapper.UserMapper">
  <!-- MapperインターフェースのfindAll関数実行時のSQL
  resultTypeには取得結果をマッピングするクラスを指定 -->
  <select id="findAll" resultType="User">
    select
      id,
      name,
      email,
      password,
      age,
      gender,
      enable_flag AS enable,
      role
    from user
  </select>
  <!-- MapperインターフェースのfindByEmail関数実行時のSQL
  parameterTypeには関数の引数の型を指定 -->
  <select id="findByEmail" parameterType="string" resultType="User">
    select
      id,
      name,
      email,
      password,
      age,
      gender,
      enable_flag AS enable,
      role
    from user
    where email = #{email}
  </select>
  <!-- Mapperインターフェースのsave関数実行時のSQL
  新規で作成する場合はinsert要素を使用する -->
  <insert id="save" parameterType="User">
    INSERT INTO user (name, email, password, age, gender, enable_flag, role)
      VALUES (#{name}, #{email}, #{password}, #{age}, #{gender}, #{enable}, #{role})
  </insert>
</mapper>



今回はselect句とinsert句のみ記述しましたが、update句とdelete句も記述可能です。
詳細についてはこちらを参考にしてください。

また、上記の例だとenable_flagカラムのみ別名enableで取得してます。
これはMyBatisが自動でJavaオブジェクトのメンバ変数名とDBカラム名を紐付けているので、Javaオブジェクトのメンバ変数名とDBカラム名が異なる場合は、Javaオブジェクト側に合わせて取得する必要があるからです。

動的のSQLの組み立てもできる

詳しくは記載しませんが、マッピングファイル上で<if>要素や<forearch>要素などを使用することで動的にSQLを組み立てることができます。

Java側でロジックを組もうとすると複雑になる場合はXML形式のマッピングファイルで動的にSQLを組み立てることでコードの読みやすくもなり、バグも減るので実際に使うことをおすすめします。

【手順6】Mapperオブジェクトのメソッドを呼び出す

では実際にSQLを実行するために、ロジック側(今回はServiceクラス)でDIコンテナに登録されているMapperオブジェクトをDIし、Mapperオブジェクトのメソッドを呼び出します。

package com.example.spring_react.springreactapp.service;

import java.util.List;

import com.example.spring_react.springreactapp.mapper.UserMapper;
import com.example.spring_react.springreactapp.model.LoginUser;
import com.example.spring_react.springreactapp.model.User;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Service:ビジネスロジックを提供するコンポーネントであることを宣言する。
 */
@Service
public class UserService implements org.springframework.security.core.userdetails.UserDetailsService {

    @Autowired
    PasswordEncoder passwordEncoder;

    /**
     * コンテナに登録されているMapperコンポーネントをインジェクションしてます
     */
    @Autowired
    UserMapper userMapper;

    @Transactional(readOnly = true)
    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        User user = userMapper.findByEmail(email);
        LoginUser loginUser = new LoginUser(user);
        return loginUser;
    }

    /**
     * 全ユーザ取得
     */
    @Transactional(readOnly = true)
    public List<User> userList() {
        return userMapper.findAll();
    }

    /**
     * ユーザ作成
     */
    @Transactional
    public void insertUser(String name, String email, String password, int age, int gender, boolean enable_flag, int role) {
        User user = new User();
        user.setName(name);
        user.setEmail(email);
        user.setPassword(passwordEncoder.encode(password));
        user.setAge(age);
        user.setGender(gender);
        user.setEnable(enable_flag);
        user.setRole(role);
        userMapper.save(user);
    }
}



Spring+Mybatisの使い方については以上になります。

MyBatisとSpring Data JPAの違い

次にMyBatisとSpring Data JPA(JPAを使いやすくしたもの)のどちらを採用するかでよく議論になるので、この2つの違いを以下にまとめたいと思います。

  • MyBatis

    JavaオブジェクトとSQLを紐付ける役割を持つ(SQLマッパー)。

    複雑ではないクエリの場合はSQLを書く必要がないので記述量を減らせる。

    逆に複雑なクエリの場合はSQLを書く必要があるが、正確にはSQLではないJPQLで記述する必要がある(JPQLについて覚える必要がある)

    JPQLでは集合演算ができなかったり、副問合せに制限があったりするので学習コストが高くなる

  • Spring Data JPA

    Javaオブジェクトとテーブルのレコードを紐付ける役割を持つ(ORマッパー)。

    単純なSQLでも記述する必要がある。

    SQLはXMLファイルに記述するので、ソースコードと分けることができる。

    正真正銘のSQLを記述するので、学習コストはかからない



一長一短ではあるとおもいますが、Spring data JPAでは複雑なSQLを記述する場合はJPQLで書かないといけないのがネックかと思います。

自分だけJPQLに詳しくなっても、他のエンジニアが扱えなかった場合を考えると無難なのはMyBatisかなとも思います。

ただ、Spring data JPAはかなり早く開発できるので複雑なSQLを使用しない場合やスタートアップの場合は採用するのもありかと思います。

検討する際の参考になれば幸いです。

ちなみにですが、Spring Data JPAの概要について知らない方は以下の記事によくまとまっているので参考にしてみて下さい。
» Spring Data JPAとは?【メリット・使い方わかります】

Spring + MyBatisの求人がどれくらいあるのか調べてみました

SpringBoot+MyBatisを使えるようになったのはいいですが、実際に働く場所がないと意味がないと思います。なので今回はどのくらいの求人数があるのかを調べてみました。

今回求人数の参考にしたのは以下のフリーランスエージェント3社です。


この3社に絞ったのはぶっちゃけ適当です笑。
(よく知られているだろう3社を適当に選びました。)

調査した結果は以下です。

検索ワードレバテックフリーランスMidworksgeechs job
Spring MyBatis0.4%(44件/10159件)4.8%(54件/1130件)0.6%(9件/1543件)
Spring4.7%(479件/10159件)4.8%(55件/1130件)6.5%(101件/1543件)
Flutter0.03%(4件/10159件)0%(0件/1130件)0.1%(2件/1543件)



Flutterは最近話題になりつつある1つのコードでiOSとアンドロイドアプリを作成できるDart言語を使用するフレームワークです。
(個人的に勉強しているので比較するために載せました)

さらにキーワード「Spring」に対する「Spring MyBatis」の割合は以下です。

検索ワードレバテックフリーランスMidworksgeechs job
「Spring」に対する「Spring MyBatis」の割合9%(44件/479件)98%(54件/55件)9%(9件/101件)



上記の結果を見てはじめに思ったのは、「Spring+MyBatis」は割と使われていると思いました。

キーワード「Spring」に対する「Spring MyBatis」の割合でMidoworksの結果98%には正直驚きました。

実際にはレバテックフリーランスとgeechs Jobの結果9%あたりなのかなとは思います。

MyBatisは使うかは別として現場だと選択肢の1つとしてよく上がる印象なのでこの結果は妥当かなと思います。

結論、知っていて損はないかなと個人的には思います。

以上、SpringBoot+MyBatisの使い方+αについてまとめました。
参考になれば幸いです。


人気記事①:
【厳選4冊+α】Spring Framework初心者におすすめな本

人気記事②:現役エンジニアがおすすめするプログラミングスクール5社:無料あり