mybatisのscriptタグを使うとSAXParseExceptionが発生する問題の調査と解決策
問題の詳細・調査
@Select({"<script>",
" select ",
" count(*) ",
" from hogeTable ht",
" where ",
" ht.number <> #{number,jdbcType=BIGINT} ",
" and ht.phone_number = #{phoneNumber,jdbcType=VARCHAR} ",
"</script>"
})
int count(@Param("phoneNumber") String phoneNumber, @Param("number") Long number);
上記のようなmybatisによるSELECT文があるとする。
mybatisのifタグが使いたいのでscriptタグでSQLを囲んでXMLタグが使用できるようにした。
試しにビルドしてみると失敗。
SAXParseExceptionが発生した。
まだXMLタグを配置すらしていないのにビルドが通らない。
scriptタグを削除すると、問題なく起動した。
問題の原因
scriptタグは囲んだ部分でXMLタグを使用可能にする。
XMLにおいて、不等号<>は特殊な意味を持つので、エスケープしないと問題が発生する。
今回の場合、
" ht.number <> #{number,jdbcType=BIGINT} ",
numberの比較条件で不等号を使っていた。
scriptタグを使用する場合、これをエスケープする必要があった。
エスケープの方法
<![CDATA[hoge]]>
で囲んでやればエスケープ可能。
" ht.number <![CDATA[<>]]> #{number,jdbcType=BIGINT} ",
のように書いて不等号をエスケープすればOK
ビルドが通ったSQL
@Select({"<script>",
" select ",
" count(*) ",
" from hogeTable ht",
" where ",
" ht.number <![CDATA[<>]]> #{number,jdbcType=BIGINT} ",
" and ht.phone_number = #{phoneNumber,jdbcType=VARCHAR} ",
"</script>"
})
int count(@Param("phoneNumber") String phoneNumber, @Param("number") Long number);
不等号をエスケープした。
修正後、SAXParseExceptionが発生せず、ビルドが通ることを確認。
まとめ
エスケープに問題があると気づくまでにかなりの試行錯誤を要した。
実際はifタグやwhereタグを使用しているSQLだったので、
タグの記法に問題があるのか?と疑って、全然真実に辿りつけなかった。
まさかエスケープが原因だとは思わなんだ……。
mybatisの動的SQLを作成しようとして、
scriptタグを使い、SAXParseExceptionが発生した場合、
不等号のエスケープ忘れが原因の可能性が高い。
ちなみに@Selectアノテーションの中にSQLを書いていく記法だが、
SQLに問題が合った場合に原因の箇所を探し辛いので、オススメできない。
今回既存のコードがそうなっていたのでそのまま使用したが、
SQLの間違い探しが超面倒だった。
ディスカッション
コメント一覧
まだ、コメントがありません