问题描述

映射接口方法如下:

 /**
* 根据姓名和年龄查询用户信息
* @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 中也是相同的。说这些,我想表达的是知识之间是有关联的,明白之间的关联就可以举一反三、触类旁通。

MyBatis 动态 SQL 语句中出现 '<' 的问题的更多相关文章

  1. Mybatis中动态SQL语句中的parameterType不同数据类型的用法

    Mybatis中动态SQL语句中的parameterType不同数据类型的用法1. 简单数据类型,    此时#{id,jdbcType=INTEGER}中id可以取任意名字如#{a,jdbcType ...

  2. mybatis实战教程(mybatis in action)之八:mybatis 动态sql语句

    mybatis 的动态sql语句是基于OGNL表达式的.可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:1. if 语句 (简单的条件判断)2. c ...

  3. Mybatis 动态Sql语句《常用》

    MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦.拼接的时候要确保不能忘了必要的空格,还要注意省掉 ...

  4. mybatis 动态sql语句(3)

    mybatis 的动态sql语句是基于OGNL表达式的.可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类: 1. if 语句 (简单的条件判断) 2. ...

  5. mybatis 动态sql语句(1)

    mybatis 的动态sql语句是基于OGNL表达式的.可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类: 1. if 语句 (简单的条件判断) 2. ...

  6. 【转】mybatis实战教程(mybatis in action)之八:mybatis 动态sql语句

    转自:除非申明,文章均为一号门原创,转载请注明本文地址,谢谢! 转载地址:http://blog.csdn.net/kutejava/article/details/9164353#t5 1. if ...

  7. 7. mybatis实战教程(mybatis in action)之八:mybatis 动态sql语句

    转自:http://www.kyjszj.com/htzq/79.html 1. if 语句 (简单的条件判断) 2. choose (when,otherwize) ,相当于java 语言中的 sw ...

  8. mybatis 动态sql语句(2)

    什么是动态SQL MyBatis的一个强大特性之一通常是它的动态SQL能力.如果你有使用JDBC或其他相似框架的经验,你就明白条件串联SQL字符串在一起是多么地痛苦,确保不能忘了空格或者在列表的最后的 ...

  9. MyBatis学习总结(11)——MyBatis动态Sql语句

    MyBatis中对数据库的操作,有时要带一些条件,因此动态SQL语句非常有必要,下面就主要来讲讲几个常用的动态SQL语句的语法 MyBatis中用于实现动态SQL的元素主要有: if choose(w ...

随机推荐

  1. hahahah JavaScript 小小小细节

    nice~!

  2. 请使用管理员权限执行pip install命令

    重要的事情说三遍! 请使用管理员权限执行pip install命令 请使用管理员权限执行pip install命令 请使用管理员权限执行pip install命令 踩坑记录 当时一切还是从一题Misc ...

  3. 案例 | 荔枝微课基于 kubernetes 搭建分布式压测系统

    王诚强,荔枝微课基础架构负责人.热衷于基础技术研发推广,致力于提供稳定高效的基础架构,推进了荔枝微课集群化从0到1的发展,云原生架构持续演进的实践者. 本文根据2021年4月10日深圳站举办的[腾讯云 ...

  4. ServletConfig和ServletContext接口

    ServletConfig 在web.xml文件中使用一个或多个init-param元素进行配置后,Tomcat初始化Servlet时,都会将该Servlet的配置信息封装到一个ServletConf ...

  5. python set 一些用法

    add(增加元素) name = set(['Tom','Lucy','Ben']) name.add('Juny') print(name)#输出:{'Lucy', 'Juny', 'Ben', ' ...

  6. [C#] NAudio 库的各种常用使用方式: 播放 录制 转码 音频可视化

    概述 在 NAudio 中, 常用类型有 WaveIn, WaveOut, WaveStream, WaveFileWriter, WaveFileReader 以及接口: IWaveProvider ...

  7. ltrim的思考

    ltrim的思考 说明 string ltrim( string $str[, string $character_mask] ) 删除字符串开头的空白字符(或其他字符) 案例1 $test = &q ...

  8. 【VsCode】常用的快捷键

    查找和替换: 查找:Ctrl +F 查找和替换:Ctrl +H编辑器和窗口管理相关快捷键: 文件之间切换:Ctrl+Tab 关闭当前窗口:Ctrl+W /Ctrl +F4 切出一个新的编辑器窗口(最多 ...

  9. Thinkphp5之Bootstrap自定义BootstrapDetailed分页插件的实现

    首先,在此目录建立一个newcrm\thinkphp\library\think\paginator\driver\BootstrapDetailed.php文件,代码如下: <?php // ...

  10. ajax提交session超时跳转页面使用全局的方法来处理

    来自:http://www.jb51.net/article/43770.htm 如果是ajax提交,超时时从服务器发出的跳转命令就不会起作用,所以如果是session超时,而且是在ajax请求,就在 ...