MyBatis 3.5 ソースコード読み歩き:Executor、動的SQL、キャッシュの境界
対象バージョン:MyBatis 3.5.x、Spring Boot 3、Java 17+。
1. SqlSessionからJDBCへ
典型的な呼び出しチェーンは次のとおりです。
SqlSession -> Executor -> StatementHandler -> JDBC Statement
-> ResultSetHandler
SqlSessionはファサードで、ExecutorはSQL実行、トランザクション、キャッシュを担当する。MappedStatementはマッピング後のSQLを、StatementHandlerはパラメータ設定と実行を、ResultSetHandlerは結果オブジェクトの構築を担当する。
2. 動的SQLとパラメータ
XMLの動的ノードは最終的にSqlSourceへ組み立てられる。#{}はパラメータマッピングを生成し、PreparedStatementを使う。${}は文字列置換なので、ホワイトリスト化したテーブル名、カラム名、ORDER BY句のフィールドにだけ使える。
OGNLは一部の動的式の評価を担う。ただし、式の表現力があることと安全が保証されることは別だ。信頼できない入力から式やSQLの構造を制御させてはならない。
3. Executor
SimpleExecutorは毎回Statementを生成し、ReuseExecutorはStatementを再利用し、BatchExecutorはバッチ更新を遅延実行し、CachingExecutorは他のExecutorをラップしてnamespace単位の2次キャッシュを提供する。
Executorを選ぶ際は、トランザクション、失敗時の再試行、メモリ、JDBCドライバを併せて考える。BatchExecutorは、単にスイッチをオンにするだけで必ず速くなるわけではない。
4. 1次キャッシュと2次キャッシュ
1次キャッシュは通常SqlSession/Executorのライフサイクル内にあり、長時間再利用すると古いデータを読み取る可能性がある。2次キャッシュはnamespaceにあるため、シリアライズ、トランザクションコミット、キャッシュ無効化の整合性を考慮する必要がある。複数のインスタンス間で強い整合性が求められる場面では、2次キャッシュをデフォルトで有効にするべきではない。
5. ソースコードの読み方
まずMapperのプロキシを追う。その後はMappedStatementの登録、Executorのデコレータチェーン、ParameterMapping、TypeHandler、ResultMapを追う。アップグレード時は、実際に固定したバージョンのソースコードを基準にし、内部フィールドを安定したAPIとして扱わないようにする。