zoukankan      html  css  js  c++  java
  • 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 中也是相同的。说这些,我想表达的是知识之间是有关联的,明白之间的关联就可以举一反三、触类旁通。
    作者:Binge
    本文版权归作者和博客园共有,转载必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    document.documentElement.clientHeight 和 $(window).height() 无法正确获取页面可视区高度
    day05python-fullstack-数据类型
    python-day05-fullstack-#判断数字,推出,使用占位符,格式化输出
    np.random.rand()函数与randn()
    numpy.dot
    Python数据分析----Matplotlib
    day03-python运算符----比较/赋值/逻辑/成员
    全栈开发-day02-python不明就里
    全栈开发-day01-字符编码与计算机容量
    matplotlib面向对象函数画图介绍:
  • 原文地址:https://www.cnblogs.com/binbingg/p/13796233.html
Copyright © 2011-2022 走看看