mybatis bind 标签 覆盖 复杂对象的某个属性值 问题。
需求: 有四个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 标签 覆盖 复杂对象的某个属性值 问题。的更多相关文章
- 【mybatis】标签条件中判断入参属性值是否包含子字符串
可以直接使用 contains判断 <foreach collection="list" item="item" index="index&qu ...
- 【java】【反射】反射实现判断发生了修改操作,判断两个对象是否发生属性值的变更,判断两个List集合内对象的属性值是否发生变更
java的反射实现: 判断发生了修改操作,判断两个对象是否发生属性值的变更,判断两个List集合内对象的属性值是否发生变更 今日份代码: package com.sxd.streamTest; imp ...
- MyBatis bind标签的用法
From<MyBatis从入门到精通> <!-- 4.5 bind用法 bind标签可以使用OGNL表达式创建一个变量并将其绑定到上下文中. 需求: concat函数连接字符串,在M ...
- mybatis bind标签
开门见山的说,平时写模糊查询,一直用${name},例如: select * from table where name like '%${name}%' 后来知道了,这样写可能会引发sql注入,于是 ...
- mybatis bind 标签
bind 标签可以使用 OGNL 表达式创建一个变量井将其绑定到上下文中.在前面的例子中, UserMapper.xml 有一个 selectByUser 方法,这个方法用到了 like 查询条件,部 ...
- java中两个对象间的属性值复制,比较,转为map方法实现
package com.franson.study.util; import java.lang.reflect.InvocationTargetException; import java.lang ...
- JSON字符串反序列化成对象_部分属性值反序列化失败
简介:本人在开发webapi接口时遇到了:一个复杂的Json字符串在反序列化为对象时报,无法发序列化其中的一个属性对象? 使用方法: InternalRecommendRequestFormModel ...
- Java反射获取对象VO的属性值(通过Getter方法)
有时候,需要动态获取对象的属性值. 比如,给你一个List,要你遍历这个List的对象的属性,而这个List里的对象并不固定.比如,这次User,下次可能是Company. e.g. 这次我需要做一个 ...
- [转]js对象中取属性值(.)和[ ]的区别
原文地址:https://www.jianshu.com/p/6a76530e4f8f 今天在写js的过程中遇到这么一个问题,取一个对象的属性值,通过obj.keys怎么都取不出来,但是用obj[ke ...
- java对象生成随意属性值
public class RandomObjectValue { public static <T> T getObject(Class<?> clazz) { T t = n ...
随机推荐
- 0627.selenium请求库*1
今日内容: 一 Selenium请求库 一 Selenium请求库 1.什么是selenium? selenium是一个自动测试工具,它可以帮我通过代码 去实现驱动浏览器自动执行相应的操作. 所以我们 ...
- Java实现台球游戏的动画实现相关代码
package com.bjsxt;//scr中新建的包packageimport java.awt.*;import javax.swing.*;public class BallGame exte ...
- 【vite+pinia】
vite 轻量快速热重载,开发环境中不需要打包操作,可以快速冷启动 按需编译,不需要等待整个应用编译完成 pinia 支持vue2和vue3,是vue专属的状态管理库,允许跨组件或者页面 共享状态 与 ...
- 宝塔Linux定时shell
定时清除缓存文件 rm -rfv /www/wwwroot/www.xxx.com/runtime 定时解压,常用于定时恢复站点 解压到当前 cd /www/wwwroot/www.xxx.com t ...
- winform 更新下载压缩文件解压并覆盖
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- 【个人笔记】从本地源部署 Office 2016 专业增强版
## 0. 大大的说明 本文使用的 Office 2016 为 Office 2016 专业增强版零售版. 零售版需要使用 Office 部署工具才可以自定义安装组件,而 VOL 版本无需部署工具即可 ...
- 获取Java运行环境信息
设备相关信息 获取设备名称 import java.net.InetAddress; import java.net.UnknownHostException; try { InetAddress l ...
- Win10系统将bat文件注册成服务
代码语法: sc create ServiceName binPath= 路径 start= auto 示例语句: sc create Tomcat binPath= F:/tomcat/bin/st ...
- 硬件IIC调试问题排查
目录 沁恒蓝牙系列芯片中目前只有CH582/583以及208包含有硬件IIC外设,本文均使用582进行测试,其他沁恒芯片也可以参考本文排查. 先进行"常规"检查,检查相关引脚的焊接 ...
- Delphi获取程序版本号
参考: http://www.delphitop.com/html/hanshu/4627.html procedure GetVersionInfo(const FileName:string; v ...