MyBatis 動態 SQL 語句中出現 '<' 的問題


問題描述

映射接口方法如下:

 /**
   * 根據姓名和年齡查詢用戶信息
   * @param name 姓名
   * @param user 獲取年齡
   * @return
   */
public List<UserEntity> selectUserByNameAndAge(@Param("name") String name, @Param("user") 		
                                               	UserEntity user);

SQL 語句映射如下:

<select id="selectUserByNameAndAge" resultMap="userResultMap">
        select * from tb_user where name = #{name} and age < #{user.age};
</select>

單元測試方法如下:

		@Test
    public void selectUserByNameAndAgeTest() {
        UserEntity user = new UserEntity();
        user.setAge(30);

        List<UserEntity> userEntities =
                userMapper.selectUserByNameAndAge("李四",user);
        System.out.println(userEntities);
        Assert.assertNotNull(userEntities);
    }

執行測試結果如下:

org.apache.ibatis.exceptions.PersistenceException: 
### Error building SqlSession.
### The error may exist in mappers/UserMapper.xml
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance.  Cause: org.xml.sax.SAXParseException; lineNumber: 29; columnNumber: 61; 元素內容必須由格式正確的字符數據或標記組成。
Process finished with exit code -1

問題分析

從報錯信息顯示,問題應該出在映射文件 UserMapper.xml 中,更具體說,是該文件的 29 行,這一行內容如下:

select * from tb_user where name = #{name} and age < #{user.age};

這正好是映射接口方法對應的 SQL 語句,這條語句結構還算簡單的,仔細看了一下似乎沒啥問題,但是為何編譯時卻報這條語句有問題呢?

首先,我們可以先假設編譯器沒有亂報錯,這條 SQL 語句確實有問題,只是我們目前不知道具體問題出在哪里?

然后,我們可以將這條 SQL 語句進行簡化,將內容修改如下:

select * from tb_user

可能你會有疑問,你這是干嘛呀,我這樣做是想驗證這條 SQL 語句到底哪里有問題。

執行測試,結果如下:

[UserEntity{id=1, userName='張三', password='123456', name='張三', age=22, sex=1, birthday=Sun Sep 02 00:00:00 IRKST 1990, created='2020-06-17 09:30:58.0', updated='2020-06-17 09:30:58.0', interests=null}, UserEntity{id=2, userName='ls', password='123456', name='李四', age=24, sex=1, birthday=Sun Sep 05 00:00:00 IRKST 1993, created='2020-06-17 09:30:58.0', updated='2020-06-17 09:30:58.0', interests=null}, UserEntity{id=5, userName='lx', password='123456', name='劉星', age=21, sex=1, birthday=Fri Jan 10 00:00:00 IRKT 1992, created='2020-06-24 17:25:23.0', updated='2020-06-24 17:25:23.0', interests=null}, UserEntity{id=6, userName='ww', password='123456', name='王五', age=21, sex=1, birthday=Fri Jan 10 00:00:00 IRKT 1992, created='2020-06-24 18:53:48.0', updated='2020-06-24 18:53:48.0', interests=null}, UserEntity{id=7, userName='zq', password='123456', name='張倩', age=21, sex=0, birthday=Mon Jul 05 00:00:00 IRKST 1993, created='2020-07-05 09:24:39.0', updated='2020-07-05 09:24:43.0', interests=null}, UserEntity{id=8, userName='ll', password='123456', name='劉麗', age=23, sex=0, birthday=Fri Jul 05 00:00:00 IRKST 1991, created='2020-07-05 09:25:44.0', updated='2020-07-05 09:25:47.0', interests=null}]

這說明簡化以后的這條 SQL 語句執行沒有問題,當然也不應該有什么問題,如果這么簡單一條 SQL 語句都有問題,那可以推測 MyBatis 開發環境可以出了問題,那么應該所有的接口方法都會報錯。

接着,我們在這條 SQL 語句基礎上再添加一些條件,如下:

select * from tb_user where name = #{name} 

執行測試,結果也沒問題,我這里就不打印出來了。

走到這里,相信你應該清楚了,通過前兩個測試,我已經縮小了問題的范圍,可以 100% 確定是 and age < #{user.age} 這里出的問題。

那究竟是哪里出了問題,我首先猜測最可能就是參數傳遞。為了驗證我的想法是否正確,我將參數傳遞寫死,如下:

select * from tb_user where name = #{name} and age < 30;

執行測試,結果如下:

org.apache.ibatis.exceptions.PersistenceException: 
### Error building SqlSession.
### The error may exist in mappers/UserMapper.xml
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance.  Cause: org.xml.sax.SAXParseException; lineNumber: 29; columnNumber: 57; 元素內容必須由格式正確的字符數據或標記組成。

Process finished with exit code -1

居然還報錯,直接打臉,這說明猜測的參數傳遞錯誤是不對的,問題原因還是沒有找到。

走到這里,估計很多初學者都快失去的耐心了,心里想這也太坑了吧,估計自己是搞不定啦。別灰心,也許我們離成功只有一步之遙。

重新整理一下思路,現在我們能夠確認的是 and age < 30 這里有問題,但是確實想不出究竟問題出在哪里。

我打算再做一次嘗試,再次修改這條 SQL 語句如下:

select * from tb_user where name = #{name} and age = 30;

和之前相比,無非就是把 ’<' 符號 改為 '=’ 符號而已,之所以這樣改,是因為也沒地方可改了,死馬當成活馬醫。

執行測試,結果如下:

[]

居然沒報錯,直接打印空集合,這真是有意想不到的收獲呀。

分析到現在,我們終於找到了問題的原因,原來搞了半天就是 '<' 符號惹的禍。

既然 '<' 符號有問題,那么自然會聯想到 ‘>' 符號是不是也有問題,修改如下:

select * from tb_user where name = #{name} and age > 30;

執行接錯,結果如下:

[]

沒報錯,說明 '>' 符號沒有問題。

還不死心,再試一試 ‘<=' 符號,結果顯示和 ’<' 符號一樣報錯。

現在問題已經再清晰不過了,'<' 符號和 '<=' 符號不能直接在映射文件中使用

問題解決

分析問題出在哪里,解決問題就變得比較簡單了。

最簡單的辦法就是百度,搜索 ”MyBatis SQL 語句中出現 ’<’ 的解決方案",相信你會找到一堆解決方案,剩下的就是測試一下哪個正確罷了,我非常鼓勵你動手去找,雖然有點麻煩,但是可以鍛煉你的獨立解決問題能力,提升自我成就感。

當然,我也會在下面提供答案,這些答案也是我在網上收集的,而且是驗證 OK

  • 解決方法一

字符轉義

以上這張表就是 MyBatis 映射文件特殊字符轉義表,'<' 符號在 XML 映射文件中其實是特殊字符,因為它和映射文件中的標記符號 ’< / >' 無法區別,所以需要用轉義字符把它替換掉。

那么,'<' 問題解決辦法如下:

select * from tb_user where name = #{name} and age &lt; #{user.age};

執行測試,正確顯示結果,問題搞定。

  • 解決方法二

使用

在使用 mybatis 時我們 SQL 語句是寫在 XML 映射文件中,如果 SQL 語句中有一些特殊的字符的話,在解析 XML 文件的時候會被轉義,但我們不希望它被轉義,所以我們可以使用來解決。

是什么,這是XML語法。在CDATA內部的所有內容都會被解析器忽略,只簡單當做字符串處理。

那么,'<' 問題解決辦法如下:

select * from tb_user where name = #{name} and age <![CDATA[ < ]]> #{user.age};

執行測試,OK

問題總結

  • 通過以上問題分析過程,其實我已經向你展示了我在實際項目開發中常用的排錯方法之一,我給它取名縮小范圍法。希望初學者能夠明白,授人以魚不如授人以漁,掌握一個方法遠比記住一個知識點更為重要。
  • 如果你懂一些 HTML 靜態網頁的知識,應該很容易想明白在映射文件中的 SQL 語句中為何 '<' 符號會報錯。而且上面那張特殊字符轉義表,你也會似曾相識,因為它也是 HTML 靜態頁面的特殊字符轉義表。的作用在 HMTL 中也是相同的。說這些,我想表達的是知識之間是有關聯的,明白之間的關聯就可以舉一反三、觸類旁通。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM