需求: 有四个sql 都需要用一个 相同的where 条件,于是定义了一个sql 标签。 然后在每个sql中使用

<include refid="myWhereSql"></include> 引入。

后来需求变更,有两个sql 的where条件有一个参数需要和其他两个不同。 于是想到了bind标签

sql 3 : <bind name = "req.cc"  value="1">

sql 4 : <bind name = "req.cc"  value="2">

List<O1> m1(@Param("req") CRequest req);  cc 为 CRequest 中的一个 属性。

这样在sql3 和 sql4 中 cc的参数就可以 不同了 。

<if test="req.cc!= null and req.cc!= ''">
and t.col1= #{req.cc}
</if>
<if test="req.dd!= null and req.dd!= ''">
and t.col2 = #{req.dd}
</if>

发现 #{req.cc} 是能正确取到值的。 于是以为解决问题了。 然而后来发现 参数dd我 传值了,但是 #{req.dd} 却取不到。

研究源码发现: MybatisDefaultParameterHandler.setParameters

                    if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {

重点是 hasAdditionalParameter 方法:

public boolean hasAdditionalParameter(String name) {
String paramName = new PropertyTokenizer(name).getName();
return additionalParameters.containsKey(paramName);
}
PropertyTokenizer 解析后的getName 返回是req (说明是按照.分割的)。 
所有req.cc 和 req.dd 都是 走这个 if 分支去获取value 值的。
value = boundSql.getAdditionalParameter(propertyName);

这里调试代码发现mybatis 会把参数生成两个map , 一个是 方法传递的参数,名称为 _paramters , 一个是额外的参数,比如bind的 名字是 req (本例) 。

这就导致 cc 和  dd 都去 从这个 名称为req的map 中获取,所以dd 是没有值的。

看来这就是一个前缀取法的坑,对于复杂对象,优先去前缀。由于先取前缀, 本来只是想覆盖其中一个属性的,这样一来,等于前缀下的所有属性都被覆盖了。 

那么如果我直接写 <bind name="cc" value = "1" > , debug 发现 他会 往 additionalParameters(注意他也是一个map) 中添加一个 key 为 cc 的 键。所以也不行。

想到一个解决方案: 既然是map , 那么能不能这样呢 ?

<bind name="_parameter.param1.cc" value="1"></bind> debug发现 cc 能取到覆盖后的值, dd 也能正常取值了。 

但是新的问题却出现了:

<if test="req.cc!= null and req.cc!= ''">
and t.col1= #{req.cc}
</if>

我把cc 设置为 null, 发现 if 判断并没有其作用。debug代码在 MybatisCachingExecutor.query 方法,然后调用MappedStatement.getBoundSql 生成 BoundSql 。其会根据 bind 标签配置 和 方法参数 共同解析生成sql 语句。

DynamicContext context = new DynamicContext(configuration, parameterObject);
rootSqlNode.apply(context);

if 判断会调用 IfSqlNode.apply() , 该方法会解析if表达式,

这里发现bind的 参数 名称尽然是 _parameter.req.cc ,而不是像处理参数那样覆盖_parameter这个map里面的req参数值。

根据这个方法,其最终会找到 _parameter中找到req , 在req中找到 cc。  这个cc 是我方法请求参数,并没有被覆盖 , 显然不是null。

结论: mybatis xml 中 if 判断  和  取值(如#{cc})使用的参数处理机制不同,导致取值能覆盖,但是if 判断不会覆盖。这算不算bug。

结论:

1. 不能用bind 覆盖复杂对象的的某个属性。

2. 为避免bug,不建议使用覆盖。建议定义新的参数名,避免有坑。

mybatis bind 标签 覆盖 复杂对象的某个属性值 问题。的更多相关文章

  1. 【mybatis】标签条件中判断入参属性值是否包含子字符串

    可以直接使用 contains判断 <foreach collection="list" item="item" index="index&qu ...

  2. 【java】【反射】反射实现判断发生了修改操作,判断两个对象是否发生属性值的变更,判断两个List集合内对象的属性值是否发生变更

    java的反射实现: 判断发生了修改操作,判断两个对象是否发生属性值的变更,判断两个List集合内对象的属性值是否发生变更 今日份代码: package com.sxd.streamTest; imp ...

  3. MyBatis bind标签的用法

    From<MyBatis从入门到精通> <!-- 4.5 bind用法 bind标签可以使用OGNL表达式创建一个变量并将其绑定到上下文中. 需求: concat函数连接字符串,在M ...

  4. mybatis bind标签

    开门见山的说,平时写模糊查询,一直用${name},例如: select * from table where name like '%${name}%' 后来知道了,这样写可能会引发sql注入,于是 ...

  5. mybatis bind 标签

    bind 标签可以使用 OGNL 表达式创建一个变量井将其绑定到上下文中.在前面的例子中, UserMapper.xml 有一个 selectByUser 方法,这个方法用到了 like 查询条件,部 ...

  6. java中两个对象间的属性值复制,比较,转为map方法实现

    package com.franson.study.util; import java.lang.reflect.InvocationTargetException; import java.lang ...

  7. JSON字符串反序列化成对象_部分属性值反序列化失败

    简介:本人在开发webapi接口时遇到了:一个复杂的Json字符串在反序列化为对象时报,无法发序列化其中的一个属性对象? 使用方法: InternalRecommendRequestFormModel ...

  8. Java反射获取对象VO的属性值(通过Getter方法)

    有时候,需要动态获取对象的属性值. 比如,给你一个List,要你遍历这个List的对象的属性,而这个List里的对象并不固定.比如,这次User,下次可能是Company. e.g. 这次我需要做一个 ...

  9. [转]js对象中取属性值(.)和[ ]的区别

    原文地址:https://www.jianshu.com/p/6a76530e4f8f 今天在写js的过程中遇到这么一个问题,取一个对象的属性值,通过obj.keys怎么都取不出来,但是用obj[ke ...

  10. java对象生成随意属性值

    public class RandomObjectValue { public static <T> T getObject(Class<?> clazz) { T t = n ...

随机推荐

  1. 0627.selenium请求库*1

    今日内容: 一 Selenium请求库 一 Selenium请求库 1.什么是selenium? selenium是一个自动测试工具,它可以帮我通过代码 去实现驱动浏览器自动执行相应的操作. 所以我们 ...

  2. Java实现台球游戏的动画实现相关代码

    package com.bjsxt;//scr中新建的包packageimport java.awt.*;import javax.swing.*;public class BallGame exte ...

  3. 【vite+pinia】

    vite 轻量快速热重载,开发环境中不需要打包操作,可以快速冷启动 按需编译,不需要等待整个应用编译完成 pinia 支持vue2和vue3,是vue专属的状态管理库,允许跨组件或者页面 共享状态 与 ...

  4. 宝塔Linux定时shell

    定时清除缓存文件 rm -rfv /www/wwwroot/www.xxx.com/runtime 定时解压,常用于定时恢复站点 解压到当前 cd /www/wwwroot/www.xxx.com t ...

  5. winform 更新下载压缩文件解压并覆盖

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  6. 【个人笔记】从本地源部署 Office 2016 专业增强版

    ## 0. 大大的说明 本文使用的 Office 2016 为 Office 2016 专业增强版零售版. 零售版需要使用 Office 部署工具才可以自定义安装组件,而 VOL 版本无需部署工具即可 ...

  7. 获取Java运行环境信息

    设备相关信息 获取设备名称 import java.net.InetAddress; import java.net.UnknownHostException; try { InetAddress l ...

  8. Win10系统将bat文件注册成服务

    代码语法: sc create ServiceName binPath= 路径 start= auto 示例语句: sc create Tomcat binPath= F:/tomcat/bin/st ...

  9. 硬件IIC调试问题排查

    目录 沁恒蓝牙系列芯片中目前只有CH582/583以及208包含有硬件IIC外设,本文均使用582进行测试,其他沁恒芯片也可以参考本文排查. 先进行"常规"检查,检查相关引脚的焊接 ...

  10. Delphi获取程序版本号

    参考: http://www.delphitop.com/html/hanshu/4627.html procedure GetVersionInfo(const FileName:string; v ...