mybatisのscriptタグを使うとSAXParseExceptionが発生する問題の調査と解決策

IT,Java

問題の詳細・調査

@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の間違い探しが超面倒だった。

IT,Java

Posted by raishin