Mybatis注解方式踩坑记录——元素内容必须由格式正确的字符数据或标记组成

mac2024-03-11  23

文章目录

背景介绍问题原因踩坑过程原始代码(可正常启动运行)错误代码(导致项目无法正常启动)修改后的正确代码(第一版)修改后的正确代码(第二版) 总结反思

背景介绍

这个月的任务有点重,一直在忙着项目的开发,导致之前都没有更新,月底了怎么着也得更一篇啊。下面进入正题 之前同事在部署系统时,无法启动,一直报这个错误,但是实在找不到原因,大概的方向就是肯定是sql文件出问题了, 因为这部分文件我有参与,所以一块看了一下,最终发现是是Mybatis注解SQL的一个坑,特此记录一下。 友情建议:一般稍微复杂一点的SQL语句还是使用xml方式比较好,便于维护、灵活度也比较高,简单的SQL可以使用注解方式,简洁明了,维护起来也不难。

问题原因

SQL中的代码存在格式问题,一般都是sql中的特殊符号转译问题,检查一下大于小于这类符号的转义写法,注意,如果是使用的注解方式书写SQL,也要检查Mapper文件,虽然日志的报错文件指向了xml文件。本次遇到的就是这个问题。

踩坑过程

原始代码(可正常启动运行)
@Select("select merchant_id as merchantId,\n" + " (select name from tb_union_merchant where id = merchant_id) as merchantName,\n" + " id as couponId,\n" + " discount_price as couponPrice,\n" + " discount_name as couponName,\n" + " discount_start as couponStart,\n" + " discount_rest as couponNum,\n" + " DATE_FORMAT(activity_end_date, '%Y-%m-%d') as endDate,\n" + " case\n" + " when (discount_rest <= 0 and DATE_FORMAT(update_date,'%Y-%m-%d') >= date_sub(curdate(),interval 2 day)) then '3'\n" + " when discount_rest >0 then '1'\n" + " else '' end as status\n"+ " from tb_discount\n" + "where del_flag = '0'\n" + " and putaway_flag = '1'\n" + " and vouchers_create = '3'\n" + " limit #{offset},#{limit};") List<Map> getDiscountListByPage(@Param("offset")int offset,@Param("limit")int limit);

以上代码为分页查询语句,中间包含了子查询和条件判断,应该是属于稍微复杂的SQL语句了。很明显,里面包含了好多转译符,可以推断出,里面注解SQL是从其他地方粘过来的(和同事核对过,这是数据库软件中写的脚本,运行成功之后才粘贴过来的)。注意一下里面的小于等于、大于等于符号,直接是使用的符号,如下图: 但是直接在注解中使用符号,项目没有报错,运行正常,说明这种脚本方式,符号是可以转译的,小于等于对应&lt=;大于等于对应&gt=;

错误代码(导致项目无法正常启动)
@Select({"<script>", "select merchant_id as merchantId,\n" + " (select name from tb_union_merchant where id = merchant_id) as merchantName,\n" + " id as couponId,\n" + " discount_price as couponPrice,\n" + " discount_name as couponName,\n" + " discount_start as couponStart,\n" + " discount_rest as couponNum,\n" + " DATE_FORMAT(activity_end_date, '%Y-%m-%d') as endDate,\n" + " case\n" + " when (discount_rest <= 0 and DATE_FORMAT(update_date,'%Y-%m-%d') >= date_sub(curdate(),interval 2 day)) then '3'\n" + " when discount_rest >0 then '1'\n" + " else '' end as status\n"+ " from tb_discount\n" + "where del_flag = '0'\n" + " and putaway_flag = '1'\n" + " and vouchers_create = '3'\n" + " <if test=\"limit != null and limit>0\" >\n" + " LIMIT #{limit} OFFSET #{offset}\n" + "</if>\n" + " <if test=\"merchantId != null and merchantId!=''\" >\n" + " merchant_id = #{merchantId}\n" + "</if>\n" , "</script>"}) List<Map> getDiscountListByPage(@Param("merchantId") String merchantId, @Param("offset") int offset, @Param("limit") int limit);

这版代码是我改的,我在原始代码加上了merchantId和limit字段的动态筛选判断,因为用到了动态SQL语句,所以遵照原始脚本语句,我就加上了脚本标识

<script>动态SQL语句</script> tips:<script>后使用逗号或者加号连接都可以

然后项目启动时,就开始报错,而且声明了报错文件是DiscountMapper.xml,而不是对应的Mapper.java文件,导致好几个同事都在xml文件中去找问题,而忽略了Mapper.java文件。

这个就是个坑点了,所以需要更改注解文件的转译写法,但是还记得上一版运行正常的SQL脚本,里面同样没有对特殊符号进行转译,但是加上script脚本之后,这样的自动转译就失效了,这是第二个坑点。

修改后的正确代码(第一版)
@Select({"<script>" + "select merchant_id as merchantId,\n" + " (select name from tb_union_merchant where id = merchant_id) as merchantName,\n" + " id as couponId,\n" + " discount_price as couponPrice,\n" + " discount_name as couponName,\n" + " discount_start as couponStart,\n" + " discount_rest as couponNum,\n" + " DATE_FORMAT(activity_end_date, '%Y-%m-%d') as endDate,\n" + " case\n" + " when (discount_rest &lt;= 0 and DATE_FORMAT(update_date,'%Y-%m-%d') &gt;= date_sub(curdate(),interval 2 day)) then '3'\n" + " when discount_rest &gt;0 then '1'\n" + " else '' end as status\n"+ " from tb_discount\n" + "where del_flag = '0'\n" + " and putaway_flag = '1'\n" + " and vouchers_create = '3'\n" + " <if test=\"limit != null and limit &gt; 0\" >\n" + " LIMIT #{limit} OFFSET #{offset}\n" + "</if>\n" + " <if test=\"merchantId != null and merchantId!=''\" >\n" + " merchant_id = #{merchantId}\n" + "</if>\n" + "</script>"}) List<Map> getDiscountListByPage(@Param("distinguish")String distinguish,@Param("merchantId") String merchantId, @Param("offset") int offset, @Param("limit") int limit);

这一版主要是将特殊符号换成转译写法 项目可以成功启动,问题解决。 但是对于注解带来的这些坑点,实在是有点可恨,我同事最终还是将这段注解方式的SQL语句换成了xml文件形式的,毕竟因为这个问题耽误了他那么久时间找原因。

修改后的正确代码(第二版)
Mapper文件 /** * 代金券列表查询 * @param merchantId * @param offset * @param limit * @return */ List<Map> getDiscountListByPage(@Param("distinguish")String distinguish,@Param("merchantId") String merchantId, @Param("offset") int offset, @Param("limit") int limit); xml文件 <!-- Start 代金券列表查询 --> <select id="getDiscountListByPage" resultType="Map"> select merchant_id as merchantId, (select name from tb_union_merchant where id = dis.merchant_id) as merchantName, id as couponId, discount_price as couponPrice, discount_name as couponName, discount_start as couponStart, discount_rest as couponNum, DATE_FORMAT(activity_end_date, '%Y-%m-%d') as endDate, case when (discount_rest &lt;= 0 and DATE_FORMAT(update_date,'%Y-%m-%d') &gt;= date_sub(curdate(),interval 2 day)) then '3' when discount_rest &gt; 0 then '1' else '' end as status from tb_discount dis <where> del_flag = '0' and putaway_flag = '1' and vouchers_create = '3' <if test=" distinguish != null and distinguish != '' " > and merchant_id in ((select t1.id from tb_union_merchant t1 where t1.distinguish = #{distinguish}) ) </if> <if test=" merchantId != null and merchantId != '' " > and merchant_id = #{merchantId} </if> </where> order by dis.create_date desc <if test="limit != null and limit > 0 " > LIMIT #{limit} OFFSET #{offset} </if> </select> <!-- 代金券列表查询 End -->

以上是最终版,将注解方式改成了xml文件格式。

总结反思

坑点 1、包含script脚本的注解SQL语句,不支持自动对特殊符号转译,如大于小于等(建议无论注解还是xml,都采用标准的转译写法比较保险)2、注解sql如果有报错,会指向对应的xml问价,误导报错文件位置(从这里也能看出,Mybatis底层在处理注解SQL文件时,还是会将它放到xml文件中处理) 总结 对于Mybatis的注解写法和xml写法,网上各有争议,当然也各有优势,不能在注解这儿踩到坑就一棍子打死,毕竟注解是xml的升级版,的确带来了很多便捷之处。但是还是建议复杂的SQL语句还是可以借助xml方式书写,因为代码维护也是个隐形工作量,哈哈,写代码也要有公德心呐。
最新回复(0)