天天看點

mybatis字元串轉義問題

問題描述

@Select("select * from account order by #{orderBy} #{orderRule} limit #{start},#{offset}")
public List<Account> getAccountList(@Param("orderBy") String orderBy, @Param("orderRule") String orderRule,
    @Param("start) int start, @Param("offset") int offset);           

複制

如上代碼所示,在執行查詢操作時,為了能夠與前端關聯進行排序,直接在SQL參數中傳遞排序字段和排序規則。

但是,在調試時偶然發現,當傳遞的“orderBy”值為不存在的字段時,竟然不會報錯!!!

經過進一步調試發現,實際上并不會按照預期的排序規則傳回資料清單!!!

原因追蹤

設定log4j的日志級别為DEBUG後發現,最終執行的SQL語句是一個預編譯操作,mybatis輸出日志如下:

==>  Preparing: select * from account order by ? ? limit ?, ? 
==> Parameters: loginName(String), DESC(String), 0(Integer), 50(Integer)           

複制

很顯然,傳遞的參數loginName和DESC是作為字元串處理的。

也就是說,很有可能mybatis對String類型的參數會進行轉換。舉個例子:傳遞的String參數為loginName,最終在SQL語句執行時為:'loginName'。

再進一步驗證,如果在SQL語句中傳遞的排序字段不是字段名loginName,而是'loginName'時,是不會按照排序規則傳回資料的,并且也不會報錯!

追溯mybatis官方文檔發現:預設情況下,使用#{}格式的文法會導緻mybatis對字元串進行修改或轉義!!!

mybatis字元串轉義問題

詳見:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#select

解決問題

将參數傳遞的文法格式#{}修改為${},即:

@Select("select * from account order by ${orderBy} ${orderRule} limit #{start},#{offset}")
public List<Account> getAccountList(@Param("orderBy") String orderBy, @Param("orderRule") String orderRule,
    @Param("start) int start, @Param("offset") int offset);           

複制

觀察mybatis日志輸出:

==>  Preparing: select * from account order by login_name DESC limit ?, ? 
==> Parameters: 0(Integer), 50(Integer)           

複制

此時,對于使用${}格式引用的參數,mybatis直接在SQL語句中插入一個不改變的字元串,而不再作為一個預編譯參數處理。

注意: 以這種方式接收使用者輸入的内容并直接提供給SQL語句作為不變的字元串是不安全的,會導緻潛在的SQL注入攻擊,是以要麼不允許使用者輸入這些字段,要麼自行轉義并檢驗。

總結

如果隻是想直接在SQL語句中插入一個不改變的字元串,比如,像ORDER BY,可以使用${}引用參數:

ORDER BY ${columnName}

,這裡mybatis不會修改或轉義字元串。