MyBatisとは?SQL中心で理解する基本構文と実務での使い方
MyBatis を使った開発では、
「SQL は自分で書く」という前提がある一方で、
その SQL をどのように MyBatis 上で管理・実行するのかを正しく理解していないケースも少なくありません。
特に実務では、
- select / insert / update / delete の基本構文
#{}と${}の違い- parameterType や resultType の意味
- resultMap によるマッピング設計
- 動的SQL(if / choose / foreach)の使い方
といった要素が頻繁に登場します。
これらは MyBatis SQL 書き方 や MyBatis 使い方 実務 といった文脈で検索されることが多く、
現場での理解不足がバグや保守性低下につながりやすいポイントでもあります。
この章では、
- MyBatis の基本構文
- なぜその書き方が必要なのか
- どう使うと設計が安定するのか
という視点から整理し、基本構文と実務で必須となる要素を解説します。
MyBatisとは何か
MyBatisの定義
MyBatis(マイバティス)とは、Java アプリケーションにおいて SQL とオブジェクトの橋渡しを行う永続化フレームワークです。
一般的に「MyBatis とは何か」と聞かれた場合、「SQL を自分で記述し、それを Java メソッドにマッピングするフレームワーク」と説明するのが最も本質的です。
Hibernate や JPA のような ORM が「オブジェクト中心」であるのに対し、MyBatis は SQL 中心設計を採用しています。
SELECT や JOIN、WHERE 条件などを開発者が明示的に書き、その結果を Java オブジェクトに変換します。
この特徴により、MyBatis は SQL の挙動を正確に把握したい業務系システムや、
既存データベース設計が重視される現場で広く利用されています。
「SQL を隠さない」という思想こそが、MyBatis の最大の特徴です。
MyBatisが解決しようとしている課題
MyBatis が解決しようとしている課題は、「JDBC を直接扱う煩雑さ」と「SQL 管理の属人化」です。
JDBC をそのまま使う場合、
- SQL の文字列管理
- パラメータ設定
- ResultSet の手動変換
- 例外処理
などをすべて自分で書く必要があり、コード量が増え、保守性も低下しやすくなります。
MyBatis はこれらを抽象化しつつも、SQL 自体は開発者が完全にコントロールできるという立ち位置を取ります。
その結果、
- SQL は XML やアノテーションで一元管理
- Java 側は Mapper メソッドとして呼び出すだけ
- 型安全性と可読性を両立
といった、実務に適したバランスを実現しています。
ORMフレームワークとの位置づけ
MyBatis はしばしば ORM フレームワークとして分類されますが、
厳密には フル ORM ではなく、SQL マッパー型フレームワークです。
ORM(Object Relational Mapping)は、
「テーブル ≒ クラス」「カラム ≒ フィールド」として自動マッピングする思想を持ちます。
一方、MyBatis は SQL → 結果 → オブジェクトという流れを明示的に定義します。
そのため、
- 複雑な JOIN
- パフォーマンス重視のクエリ
- DB 方言への最適化
といったケースでは、MyBatis の方が柔軟に対応できます。
SQLとは何か
SQLの役割と基本概念
SQL(Structured Query Language)とは、リレーショナルデータベース(RDB)を操作するための標準言語です。
アプリケーション開発において SQL は、「データをどう保存し、どう取り出すか」を直接指定する唯一の手段であり、データ層の根幹を担います。
SQL の基本的な役割は、大きく以下の4つに分けられます。
- SELECT:データの取得
- INSERT:データの追加
- UPDATE:データの更新
- DELETE:データの削除
これらは総称して CRUD 操作と呼ばれ、どのような Web アプリケーションであっても必ず登場します。
フレームワークや言語が変わっても、最終的にデータベースに対して実行されるのは SQL です。
重要なのは、SQL が「単なる取得文」ではなく、結合(JOIN)、集計(GROUP BY)、条件分岐(WHERE) などを通じて、業務ロジックの一部を担うことも多い点です。
そのため SQL は、アプリケーション設計と密接に結びついた技術であり、切り離して考えることはできません。
アプリケーションとSQLの関係
Web アプリケーションにおいて、SQL はユーザー操作とデータをつなぐ「最終実行レイヤー」に位置します。
ユーザーが画面で検索条件を入力した場合、その条件は最終的に SQL の WHERE 句としてデータベースに伝えられます。
一般的な Java アプリケーションでは、
Controller → Service → 永続化層 → SQL → DB
という流れで処理が進みます。
この中で SQL は、アプリケーションの内部実装に隠れているように見えて、実行結果に最も直接的な影響を与える存在です。
たとえば、同じ「ユーザー一覧を取得する」という要件でも、
- JOIN の書き方
- インデックスの有無
- 条件指定の順序
によって、パフォーマンスや結果の正確性は大きく変わります。
つまり、SQL は単なる裏方ではなく、アプリケーション品質を左右する重要な要素です。
SQLを直接書くことのメリットと課題
SQL を直接書く最大のメリットは、処理内容を完全に把握できることです。
ORM のように内部で自動生成される SQL と違い、実行される文が明示されるため、挙動が非常に分かりやすくなります。
特に、
- パフォーマンスが重要な処理
- 複雑な集計・結合
- DB 固有の最適化
が求められる場面では、SQL を直接制御できることが大きな強みになります。
一方で、SQL をそのままアプリケーションコードに埋め込むと、
- SQL が散在しやすい
- パラメータ設定が煩雑
- SQL インジェクションのリスク
- Java オブジェクトとの変換処理が冗長
といった課題も生じます。
MyBatis は、これらの「SQL を直接書くことの弱点」を補いながら、SQL の自由度を失わないことを目的としたフレームワークです。
MyBatisとSQLの関係
MyBatisはSQLを「書く」フレームワーク
MyBatis を一言で表すなら、「SQL を書くことを前提にした永続化フレームワーク」です。
JPA や Hibernate のように、エンティティ定義から SQL を自動生成する思想とは明確に異なります。
MyBatis では、開発者が SQL を XML やアノテーションとして明示的に記述し、
それを Java の Mapper メソッドと結び付けます。
つまり、SQL の設計責任は常に開発者側にあります。
この設計により、
- 実行される SQL が明確
- レビュー時に SQL を直接確認できる
- 想定外のクエリが生成されない
というメリットが生まれます。
MyBatisがSQLと相性が良い理由
MyBatis が SQL と相性が良い理由は、SQL の構造をそのまま活かせる点にあります。
JOIN やサブクエリ、CASE 文など、RDB が本来得意とする表現を制限なく使えます。
また、
- DB 設計が厳密に決まっている
- 既存 SQL が大量に存在する
- DB チューニングが前提の案件
といったケースでは、MyBatis は非常に現実的な選択肢になります。
SQL を主役として扱いたい設計では、MyBatis の思想は自然にフィットします。
MyBatisの主な特徴
SQLを明示的に管理できる
MyBatis の最大の特徴は、SQL をフレームワーク内部に隠さず、開発者が明示的に管理できる点にあります。
ORM 系フレームワークでは、エンティティ定義を元に SQL が自動生成されることが多く、実際にどのようなクエリが発行されているのかを把握しづらい場合があります。
一方 MyBatis では、実行される SQL はすべて XML やアノテーションとして定義され、
「どの処理で、どの SQL が使われているか」が非常に明確です。
これはレビューや障害対応の場面で大きなメリットになります。
特に業務系システムでは、
- 複雑な JOIN
- 業務ロジックに密接した集計 SQL
- DB 設計に依存したクエリ
が多く、SQL をブラックボックスにするとかえってリスクが高まります。
MyBatis は SQL を第一級の設計要素として扱うため、
DB・SQL・アプリケーションの責務を明確に分離したい設計と非常に相性が良いフレームワークです。
XML / アノテーションによる柔軟な記述
MyBatis は、SQL 定義の方法として XML とアノテーションの両方をサポートしています。
これにより、プロジェクトの規模や SQL の複雑さに応じた使い分けが可能になります。
一般的には、
- 複雑な SQL・動的 SQL → Mapper XML
- 単純な CRUD 処理 → アノテーション
という使い分けがよく採用されます。
XML では SQL をほぼそのまま記述できるため、可読性が高く、DB エンジニアとのレビューもしやすくなります。
一方で、アノテーションを使えば Java コード側に SQL を集約でき、小規模な処理ではファイル数を増やさずに実装できます。
このように MyBatis は「すべてを XML に寄せる」「すべてをアノテーションで書く」といった極端な設計を強制せず、現実的な柔軟性を提供しています。
MyBatisの基本構成
Mapperインターフェース
Mapper インターフェースは、Java 側から MyBatis を利用するための入口です。
SQL を直接呼び出すのではなく、メソッドとして定義することで、安全かつ型安全に SQL を実行できます。
Mapper インターフェースには SQL の実装は書かず、「どんな操作を行うか」だけを宣言します。
実装は Mapper XML やアノテーションと紐づけられます。
この構造により、
- SQL と Java コードの責務分離
- テストのしやすさ
- DI との親和性
が確保されます。
実務では、この Mapper 設計が MyBatis の使いやすさを大きく左右します。
Mapper XML の役割
Mapper XML は、MyBatis における SQL 定義の中心的存在です。
ここに select / insert / update / delete を定義し、Mapper インターフェースのメソッドと対応付けます。
XML を使うことで、
- SQL をほぼ生の形で記述できる
- 動的 SQL を整理して管理できる
- SQL 修正が Java コードに影響しにくい
といったメリットがあります。
業務ロジックが複雑なシステムほど、この XML 分離の価値は高くなります。
ResultMapとオブジェクトマッピング
ResultMap は、SQL の実行結果を Java オブジェクトにマッピングするための仕組みです。
カラム名とフィールド名が一致しない場合や、
JOIN 結果を一つのオブジェクトにまとめたい場合に特に重要になります。
単純な resultType では対応しきれないケースでも、ResultMap を使えば柔軟なマッピングが可能です。
Spring / Spring Bootとの連携
現在の Java 実務では、MyBatis は Spring Boot と組み合わせて使われるのが一般的です。
Spring Boot Starter for MyBatis を利用すれば、設定は最小限で済み、Mapper の DI も自動で行われます。
これにより、
- Controller → Service → Mapper
- トランザクション管理
- 設定ファイルの一元管理
といった、Spring らしい設計をそのまま適用できます。
MyBatisの基本構文とよく使う要素
select / insert / update / delete
MyBatis における SQL 定義の基本は、select / insert / update / delete の4種類です。
select の例
<select id="findById" parameterType="long" resultType="com.example.entity.User">
SELECT id, name, email
FROM users
WHERE id = #{id}
</select>insertの例
<insert id="insertUser" parameterType="com.example.entity.User">
INSERT INTO users (name, email)
VALUES (#{name}, #{email})
</insert>
updateの例
<update id="updateUser">
UPDATE users
SET name = #{name}
WHERE id = #{id}
</update>deleteの例
<delete id="deleteUser">
DELETE FROM users
WHERE id = #{id}
</delete>#{ } と ${ } の違い
MyBatis を学ぶ上で必ず理解しておくべきなのが、#{} と ${} の違いです。
この2つは見た目が似ていますが、役割と安全性がまったく異なります。
#{ }(プレースホルダ)
WHERE id = #{id}#{} は PreparedStatement のプレースホルダとして扱われ、
SQL 実行時に値が安全にバインドされます。
- SQL インジェクション対策が有効
- 型変換が自動で行われる
- 通常はこちらを使うのが原則
${ }(文字列置換)
ORDER BY ${sortColumn}${} は単なる 文字列置換です。
SQL としてそのまま展開されるため、非常に危険な側面があります。
- SQL インジェクションのリスクが高い
- 値の妥当性チェックが必須
実務では、
「基本は #{ }、${ } はどうしても必要な場合のみ」というルールを徹底することが重要です。
parameterType と resultType
MyBatis では、
SQL に渡す値と、SQL の結果を受け取る型を明示的に指定します。
parameterType
<select id="findById" parameterType="long">- SQL に渡される引数の型
- プリミティブ型 / DTO / Map など指定可能
- 複数パラメータの場合はオブジェクト化が一般的
resultType
<select id="findAll" resultType="com.example.entity.User">- SQL の結果1行をマッピングする型
- カラム名とプロパティ名が一致している必要あり
単純なケースでは resultType で十分ですが、
カラム名とプロパティ名が一致しない場合には、次に説明する resultMap が必要になります。
resultMap の基本
resultMap は、
SQL の結果と Java オブジェクトの対応関係を明示的に定義する仕組みです。
<resultMap id="userResultMap" type="com.example.entity.User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<result property="email" column="email_address"/>
</resultMap>
<select id="findUser" resultMap="userResultMap">
SELECT
user_id,
user_name,
email_address
FROM users
</select>このように定義することで、
- SQL 側のカラム命名を自由にできる
- 既存テーブル構造に引きずられない
といったメリットがあります。
実務では、
少しでも複雑な SQL になったら resultMap を使うという判断が、長期的な保守性につながります。
動的SQL(if / choose / foreach)
MyBatis の大きな強みの一つが、XML 上で動的に SQL を組み立てられる点です。
if の例
<select id="searchUsers" resultType="User">
SELECT * FROM users
WHERE 1=1
<if test="name != null">
AND name = #{name}
</if>
</select>choose(条件分岐)
<choose>
<when test="status == 'ACTIVE'">
AND status = 'ACTIVE'
</when>
<otherwise>
AND status = 'INACTIVE'
</otherwise>
</choose>foreach(IN 句)
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>これにより、
- 条件検索
- 可変長 IN 句
- フィルタ条件の組み合わせ
といった実務で避けられない要件を、Java 側で無理に組み立てることなく SQL として表現できます。
まとめ|MyBatisの基本構文を理解することが実務力につながる
MyBatis は、SQL を完全に隠蔽する ORM とは異なり、
開発者が SQL を明示的に書き、制御できる永続化フレームワークです。
そのため、select / insert / update / delete といった基本構文を正しく使い分けることが、MyBatis 実務における品質を大きく左右します。
特に、#{}による安全なパラメータバインディング、resultType と resultMap の使い分け、
そして 動的SQL(if / foreach)を適切に使うことで、
MyBatis SQL 可読性・保守性・パフォーマンスを高い水準で維持できます。
MyBatis の基本構文は一見シンプルですが、
理解が浅いまま使うと SQL インジェクションや設計の破綻を招きやすい技術でもあります。
だからこそ、MyBatis 使い方・MyBatis SQL 設計・Spring Boot MyBatis 連携を
体系的に理解することが、長く通用するエンジニアスキルにつながります。
