JavaとMyBatisの相性を考える|〜「SQLを書ける強さ」を、Javaの設計力で安全に引き出す〜

Java

Javaでデータベースを扱うとき、選択肢が多くて迷います。

  • なるべく早く作りたい
  • パフォーマンスも気になる
  • 既存の複雑なテーブルに合わせないといけない
  • SQLをしっかり管理したい
  • でも、安全性は絶対に落としたくない

こういう現場の悩みに、ちょうどハマりやすいのが MyBatis です。

MyBatisは一言でいうと、
「SQLを自分で書きつつ、JDBCの面倒をほぼ消して、Javaの型にきれいに戻してくれる仕組み」

そして、ここが本題です。

JavaとMyBatisは相性がいい
理由は「便利だから」ではなく、Javaの設計思想(責務分離、型、安全な実装)と、MyBatisのSQL主導がピタッと噛み合うからです。

この記事では、初心者でも腹落ちするように、ゆっくり解説します。

  • MyBatisが向く場面
  • JPAとの考え方の違い
  • Spring / Spring Bootと組み合わせたときの強み
  • 事故りやすいポイント(特に ${}
  • チーム開発で安全に運用するコツ

MyBatisは「SQLを捨てない」選択

JavaのDBアクセスというと、最初にこういう印象を持つ人が多いです。

  • ORMは便利そう
  • SQLはなるべく書かないほうがよさそう

でも現場には、SQLを避けられない理由が普通にあります。

  • 集計や結合が多い
  • 既存DBの都合でテーブル設計が複雑
  • 実行計画やインデックスを見ながら調整したい
  • SQLが仕様そのもの(レビュー対象)

こういうとき、「SQLは書かない」よりも
**「SQLを安全に、読みやすく、管理できる」**ことが大事になります。

MyBatisは、そこに強いです。


JavaとMyBatisが相性いい理由

相性の良さは、ふわっとした話ではありません。理由があります。

Javaの強みは「境界をきれいに作れる」こと

Javaは、インターフェースや型で、役割をはっきり分けるのが得意です。

  • どこからどこまでがDBアクセスか
  • どこからが業務ロジックか
  • 何を入力して何を返すか

これがコードとして見えます。

MyBatisも、まさにその形に合います。
Mapperという境界が作れるからです。

MyBatisの強みは「SQLの責務を外に出せる」こと

SQLをサービスの中にべったり書くと、こうなります。

  • ロジックとSQLが混ざる
  • テストしづらい
  • 変更の影響が読めない

MyBatisは、SQLをMapper側に寄せて、Java側の責務を軽くできます。
結果として、「Javaらしい設計(読みやすい、直しやすい)」になります。


最小構成でイメージするMyBatis

「結局、何がどうなるの?」をイメージするために、最小の形を見ます。

役割の分担

  • サービス層:業務の流れをまとめる
  • Mapper:DBに何を聞くかを定義する
  • SQL(XMLなど):実際の問い合わせを書く

この分割が、Javaの得意な「責務分離」と一致します。


実装例:サービスとMapperのきれいな分離

サービス層の例(依存注入でスッキリ)

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    private final UserMapper userMapper;

    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Transactional
    public UserDto findUser(UserId id) {
        return userMapper.selectUser(id);
    }
}

ここで見てほしいのは、サービスがやっているのは「流れ」であって、SQLの詳細ではない点です。
読み手が安心する構造です。

Mapperインターフェースの例

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    UserDto selectUser(UserId id);
}

これだけ見ると、「SQLはどこ?」となりますよね。
SQLは、XML側に置く形がよく使われます。


実装例:SQLを外に出す(XMLのイメージ)

<mapper namespace="com.example.UserMapper">

  <select id="selectUser" parameterType="com.example.UserId" resultType="com.example.UserDto">
    SELECT
      user_id,
      user_name
    FROM
      users
    WHERE
      user_id = #{value}
  </select>

</mapper>

ここで注目ポイントは #{} です。
この #{} は、MyBatisを安全に使う上で超重要です。


ここが最重要:#{} と ${} の違い

MyBatisで事故が起きるとしたら、だいたいここです。

#{} は「値」として渡す

#{} は、SQLの中で として扱われやすい書き方です。
安全に使いやすいのはこちらです。

${} は「文字列として差し込む」

${} は、文字をそのままSQLに差し込むような形になりやすいです。
つまり、入力を間違えると SQLインジェクションに直結します。

ここは強く言います。

基本は #{}
${} は「分かっていて、必要なときだけ」です。


${} を使いたくなる場面と、安全な考え方

とはいえ現場では、こういう要望が出ます。

  • 並び順を選べる検索にしたい
  • 検索対象のカラムを切り替えたい

このとき、値として渡せないもの(カラム名や並び順)は、つい ${} を使いたくなります。

でも、ここで「ユーザー入力をそのまま ${}」は危険です。

安全にする基本は「ホワイトリスト」

つまり、使ってよい候補を サーバ側で決め打ちします。

例として、並び順を「決められた候補だけ」にします。

  • 名前順
  • 作成日時順
  • 更新日時順

そして、その候補をJava側で選ばせます。
「自由入力」にしないのがコツです。


動的SQLは強い。だからこそ「型」を作る

MyBatisの強みのひとつが動的SQLです。
検索条件が増えたり、条件でWHERE句が変わったりする場面で便利です。

でも、便利すぎてこうなりがちです。

  • 条件分岐が増えすぎる
  • SQLが読みにくくなる
  • 仕様変更のたびに怖くなる

ここでJavaの出番です。

条件を「まとまり」として表現する

検索条件をバラバラの引数で渡すのではなく、条件用のクラスにまとめます。

  • 入力が増えても拡張しやすい
  • バリデーションしやすい
  • SQL側も読みやすい

この「型を作って守る」考え方は、まさにJavaが得意です。


MyBatisとJPAの違いは「設計の主役」が違う

対立ではなく、得意分野が違います。

  • JPA:オブジェクト中心に設計しやすい。CRUDに強い
  • MyBatis:SQL中心に設計しやすい。複雑クエリに強い

ここで大事なのは「どっちが上」ではなく、要件とチームの勝ち筋に合うかです。

比較表(文字だけで整理)

観点MyBatisJPA
主役SQLオブジェクト
強い場面複雑な検索、集計、既存DBCRUD中心、ドメイン設計
読み方SQLレビューがしやすいモデル設計が効く
注意点${} と動的SQLの暴走取得戦略や発行SQLの見えにくさ

Spring / Spring Bootと合わせると強くなる理由

MyBatis単体でも便利ですが、Springと合わせるとさらに強くなります。

  • 依存注入でMapperをきれいに扱える
  • トランザクション管理をSpringに寄せられる
  • サービス層が読みやすくなる
  • テストがしやすくなる

つまり、MyBatisのSQL力を、Springの設計力で安全に包むイメージです。


よくある「つまずき」と回避策

つまずき:Mapperが肥大化する

SQLを全部Mapperに集めると、Mapperが巨大になりがちです。

回避策はシンプルです。

  • 「用途ごと」にMapperを分ける
  • ひとつのSQLの責務を小さく保つ
  • 名前で意図が伝わるようにする

つまずき:いわゆるエヌプラスワン問題

取得の仕方によって、DBアクセスが増えすぎることがあります。
これはMyBatisでも起きます。

回避策は、レビューの観点を持つことです。

  • 「この処理、何回DBに行く?」を意識する
  • SQLを一度で取れる形に寄せる
  • まとめて取る設計を優先する

つまずき:動的SQLが読めなくなる

動的SQLは便利ですが、分岐が増えると地獄になります。

回避策は、

  • 条件クラスを作る
  • SQLの分岐を増やしすぎない
  • 「分岐の型」をチームで統一する

です。


チーム開発でのMyBatis運用ルール

MyBatisは「SQLが書ける人だけが強い」と言われることがあります。
でもそれは、ルールがないチームで起きやすい話です。

ルールがあると、MyBatisはチーム全体の武器になります。

ルール例(すぐ効くもの)

  • パラメータは基本 #{} にする
  • ${} を見つけたら「安全な根拠」を必ず説明する
  • SQLの命名規則を決める
  • Mapperの責務範囲を決める
  • 大きすぎるSQLは分割や見直しを検討する

レビューで使えるチェックリスト

最後に、レビューでそのまま使える形にまとめます。

  • 入力が入る場所は #{} になっているか
  • ${} を使っているなら、候補が固定されているか(自由入力ではないか)
  • SQLの責務が大きすぎないか(読める大きさか)
  • Mapperとサービスの責務が混ざっていないか
  • DBアクセス回数が増えすぎる形になっていないか

この視点があるだけで、MyBatisはかなり安全に運用できます。


まとめ:JavaとMyBatisは「設計」と「SQL」が噛み合う

JavaとMyBatisの相性がいい理由を、もう一度まとめます。

  • Javaは責務分離と型で、設計を守れる
  • MyBatisはSQLを明示でき、現場の要件に強い
  • Springと組み合わせると、管理と安全性が上がる
  • 最大の注意点は ${}。ここを制御できれば強い

つまり、MyBatisは「雑に使うと危ない」。
でも「理解して使うと、とても強い」。
そしてJavaは、その「理解して安全に使う」ための土台が強い言語です。


絶対にJavaプログラマーになりたい人へ

MyBatisを使いこなす力は、単なるSQL力ではありません。
設計力、レビュー力、安全性の意識がセットで問われます。

もしあなたが、

  • Javaで仕事がしたい
  • DBアクセスで評価されたい
  • 安全で読みやすい設計を身につけたい

と思うなら、まずは自己学習の軸を固めましょう。

おすすめはこちらです。
絶対にJavaプログラマーになりたい人へ。
https://amzn.asia/d/3E1CYbv

それでも、

  • 自分のSQLやMapper設計が正しいか不安
  • コードレビューをしてほしい
  • 転職も含めてサポートが欲しい

という方は、こちらも検討してみてください。

サイゼントアカデミー
https://academy.cyzennt.co.jp

自己学習で身につけた力を、現場で通用する形に整える場所としておすすめです。

コメント

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