MyBatis とは？ Java初学者のための基礎解説

# はじめに
「Railsの『**[ActiveRecord](https://railsguides.jp/active_record_basics.html)**』やLaravelの『**[Eloquent](https://readouble.com/laravel/11.x/ja/eloquent.html)**』などのORM(O/Rマッピング)に慣れて、あまりSQLを書くことがない」ということはありませんか？

この記事は、SQLの基礎知識を学んだ方、そしてJava初学者を対象に、MyBatisの基本的な使い方を解説します。実践に活かしやすいように必要最低限の情報だけをまとめました。

# MyBatisとは
MyBatisは、**Javaアプリケーションからデータベースを操作する**ための永続化フレームワークです。SQLを**XMLファイルまたはアノテーション**で記述することで、[JDBC（Java Database Connectivity）](https://docs.oracle.com/javase/jp/7/technotes/guides/jdbc/index.html)を直接操作する手間を省き、**オブジェクトとリレーショナルデータベース間のマッピング**を容易にします。

## MyBatisの特徴
主な特徴は以下の通りです。
 * **SQLの分離**: JavaコードからSQLを分離し、XMLファイルに記述することで、SQLの管理がしやすくなります
 * **柔軟なSQL記述**: 動的なSQLを記述できるため、条件に応じた複雑なクエリも簡単に扱えます
 * **シンプルなマッピング**: データベースの行をJavaオブジェクトに自動的にマッピングできます
 * **高いパフォーマンス**: 必要に応じて手動でSQLを最適化できるため、パフォーマンスを追求しやすいです

# ORMとSQLマッピングの違い
## ORM (Object-Relational Mapping)
ORMは、データベースのテーブルをプログラミング言語の**オブジェクト**（**クラス**）として直接扱うための技術です。開発者はSQLを直接書く代わりに、**オブジェクトの操作を通じてデータベースと対話します。**

## SQLマッピング
**開発者がSQLを直接記述**し、そのSQLの結果をプログラミング言語の**オブジェクトに「マッピング」する**ことを主な役割とする技術です。

## ORMとSQLマッピングの比較表
|特徴|	ORM（オブジェクトリレーショナルマッピング）|	SQLマッピング(SQL Mapper)|
|--|--|--|
|SQLの扱い|	自動生成される。開発者は原則書かない。|	開発者が直接記述する。|			
|抽象化|	高い。オブジェクト中心の思考。|	低い。SQLとデータベース構造を意識する。|			
|開発速度|	定型的なCRUDでは非常に速い。|	定型的なCRUDではORMより遅い。複雑なクエリでは速い場合がある。|			
|パフォーマンス|	自動生成SQLの非効率性からボトルネックになる可能性あり。チューニングはフレームワークの知識が必要。|	SQLを直接書くため、最適化しやすい。開発者のSQLスキルに依存。|			
|DBの独立性|	高い。フレームワークが吸収する。|	低い。DB固有のSQLを書くと切り替えが難しい。|		
|学習コスト|	フレームワークの複雑な概念を学ぶ必要がある。|	SQLの知識が必須。マッピングの概念は比較的シンプル。|			
|最適なユースケース|	オブジェクト指向的な設計を重視し、迅速な開発が必要なシステム。|	複雑なクエリ、パフォーマンス要求の高い部分、既存DBとの連携が必要なシステム。	|
|代表例|Hibernate/JPA(Java), ActiveRecord(Ruby on Rails), Eloquent ORM(Laravel), SQLAlchemy(Python)|MyBatis(Java), Spring JDBCテンプレート(Java), jOOQ（Java）,Dapper(.NET)|


# 開発環境
この記事では、以下の環境を想定して解説を進めます。
 * Java: OpenJDK 17
 * Spring Boot: 3.x系 ([MyBatis-Spring-Boot-Starter](https://github.com/mybatis/spring-boot-starter)を利用)
 * MyBatis: 3.x系
 * データベース: postgresql
 
# 設定
Spring BootアプリケーションでMyBatisを利用する場合、主にapplication.propertiesまたはapplication.ymlファイルでデータベース接続情報を設定します。

## application.properties
```
spring.datasource.url=jdbc:postgresql://db:5432/dev
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=org.postgresql.Driver

mybatis.mapper-locations=classpath:/mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true
```

## application.yml
```
spring:
  datasource:
    url: jdbc:postgresql://db:5432/dev
    username: username
    password: password
    driver-class-name: org.postgresql.Driver
mybatis:
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
```


# Mapper XMLファイル
MyBatisでは、SQL文をXMLファイルに記述するのが一般的です。これらのファイルをMapper XMLファイルと呼びます。通常、`src/main/resources`配下に配置します。
例: UserMapper.xml
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">

</mapper>
```
**namespace属性**には、通常対応するJavaの**Mapperインターフェースの完全修飾名**を指定します。

## sql
sqlタグを使用すると、**複数のクエリで共通して使用するSQLフラグメント**を定義できます。これにより、SQLの再利用性を高めることができます。
```xml
<sql id="userColumns">
    id, name, email
</sql>

<select id="selectUserById" resultType="com.example.demo.model.User">
    SELECT
    <include refid="userColumns"/>
    FROM users
    WHERE id = #{id}
</select>
```

## resultMap
resultMapは、**データベースで取得した結果セットからJavaオブジェクトへのマッピング**を詳細に定義するために使用します。特に、<INS>テーブルの列名とJavaオブジェクトのプロパティ名が異なる場合や、複合オブジェクト（ネストされたオブジェクトやコレクション）をマッピングする場合</INS>に強力です。

```xml
<resultMap id="userResultMap" type="com.example.demo.model.User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <result property="email" column="user_email"/>
</resultMap>
```

 * `id`: 主キーとなるプロパティと列をマッピングします
   * `property`: Javaオブジェクトのプロパティ名
   * `column`: データベースの列名
 * `result`: 主キー以外のプロパティと列をマッピングします。idタグと同様にpropertyとcolumn属性を使用します
 
## association
associationは、あるオブジェクトが他の**単一のオブジェクトを含む場合**（1対1の関係）にマッピングを定義します。
```xml
<resultMap id="userAddressResultMap" type="com.example.demo.model.User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <association property="address" javaType="com.example.demo.model.Address">
        <id property="id" column="address_id"/>
        <result property="street" column="street_name"/>
        <result property="city" column="city_name"/>
    </association>
</resultMap>
```

## collection
collectionは、あるオブジェクトが**他のオブジェクトのコレクションを含む場合**（1対多の関係）にマッピングを定義します。
```xml
<resultMap id="blogResultMap" type="com.example.demo.model.Blog">
    <id property="id" column="blog_id"/>
    <result property="title" column="blog_title"/>
    <collection property="posts" ofType="com.example.demo.model.Post">
        <id property="id" column="post_id"/>
        <result property="title" column="post_title"/>
        <result property="content" column="post_content"/>
    </collection>
</resultMap>
```

## select
selectタグは、データベースからデータを取得するためのSQLクエリを定義します。
```xml
<select id="findAllUsers" resultType="com.example.demo.model.User">
    SELECT id, name, email FROM users
</select>

<select id="findUserById" parameterType="int" resultMap="userResultMap">
    SELECT user_id, user_name, user_email FROM users WHERE user_id = #{id}
</select>
```

 * `id`: Mapperインターフェースのメソッド名と一致させます
 * `parameterType`: **SQLクエリに渡される引数の型**を指定します。Javaのプリミティブ型や完全修飾クラス名を指定できます。省略することも可能です
 * `resultType`: **クエリの結果をマッピングするJavaオブジェクトの完全修飾クラス名**を指定します。データベースの列名とJavaオブジェクトのプロパティ名が一致する場合に便利です。
 * `resultMap`: resultTypeの代わりに**resultMapのID**を指定することで、より詳細なマッピングルールを適用できます

## insert, update, delete
insert, update, deleteタグは、それぞれデータベースにデータを挿入、更新、削除するためのSQLクエリを定義します。
```xml
<insert id="insertUser" parameterType="com.example.demo.model.User" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO users (name, email) VALUES (#{name}, #{email})
</insert>

<update id="updateUser" parameterType="com.example.demo.model.User">
    UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}
</update>

<delete id="deleteUser" parameterType="int">
    DELETE FROM users WHERE id = #{id}
</delete>
```
 * `useGeneratedKeys`: データベースが自動生成するキー（例: オートインクリメントのID）をMyBatisが取得し、指定されたkeyPropertyにセットするかどうかを指定します
 * `keyProperty`: 自動生成されたキーをセットするJavaオブジェクトのプロパティ名を指定します

# おわりに
**ORM**（**O/Rマッピング**）による基本的なCRUD操作は、[SQLを書かずに実装できる](https://qiita.com/gomiryo/items/6d448c500749f91242d2#sql%E3%82%92%E7%9F%A5%E3%82%89%E3%81%AA%E3%81%8F%E3%81%A6%E3%82%82crud%E5%87%A6%E7%90%86%E3%81%8C%E6%9B%B8%E3%81%91%E3%82%8B)のでスピーディーに開発するのに適しています。
対してMyBatisのような**SQLマッピング**は、開発者がSQLを直接記述する自由度と引き換えに、データベース操作のより細かい制御とパフォーマンスの最適化が可能です。
システム全体でどちらか一方だけを使うのではなく、両者を組み合わせる「ハイブリッド」なアプローチも大切です。

# 参考記事
 * MyBatis 3 | Documentation
https://mybatis.org/mybatis-3/ja/index.html?hl=ja-JP