电子商务网站开发教程书内代码,wordpress数据库作用,提高网站互动性,设计网站公司收费现象
最近有个需求#xff0c;需要在mybatis对数据库进行写入操作的时候#xff0c;根据条件对对象中的某个值进行置空#xff0c;然后再进行写入#xff0c;这样数据库中的值就会为空了。
根据网上查看的资料#xff0c;选择在 StatementHandler 类执行 update 的时候进…现象
最近有个需求需要在mybatis对数据库进行写入操作的时候根据条件对对象中的某个值进行置空然后再进行写入这样数据库中的值就会为空了。
根据网上查看的资料选择在 StatementHandler 类执行 update 的时候进行对参数的拦截
修改参数完毕后再调用 statementHandler.getParameterHandler().setParameters 方法将修改后的值重新 set 进去,
此时出现了问题发现mybatis在控制台log中打印出来的参数竟然多了一份下面是临摹的情况没有实际用update 而是拦截的query方法简化了一下逻辑
这里我调用了3次 setParameters 加上mybatis本身的一次输出了4个 Parameters 实际上我的xml中只接收了一个参数并且虽然log多打印了一些参数实际对我的结果并无影响。 为什么会发生这个问题
问题就出在 StatementHandler.getParameterHandler().setParameters 这个方法上这里是对本次数据库操作的参数进行赋值
这过程中有一个 typeHandler.setParameter(ps, i 1, value, jdbcType) 操作
这里会根据相应的类型处理器进行赋值这个过程如下typeHandler进行赋值操作
这时候会调用 jdbc 的 PreparedStatement.setXxx 方法但是 mybatis对 PreparedStatement 做了一个拦截 这个拦截就是日志记录类 PreparedStatementLogger 当调用setXxx方法时 Logger类会调用 setColumn 方法这个setColumn方法就是记录本次入参情况 最终调用 PreparedStatement.executeXXX方法时本次代理将会根据条件决定要不要打印出参数等log 而这个参数log就是前面我们提到的setColumn方法中存入的 columnValues 属性 可以看到这个属性使用的是 ArrayList 这也就说明了为什么会重复打印参数log出来这意味着当你多次调用 StatementHandler.getParameterHandler().setParameters 这个方法时columnValues 不会清除之前记录的参数并且继续保存你这次重新set的参数进来。 如何解决
如果mybatis不使用ArrayList存值是否就可以避免这个问题并且columnNames和columnValues这两个值仅仅在打印log的时候使用并没有在其他地方有使用到
而columnMap 刚好起到了就算多次调用StatementHandler.getParameterHandler().setParameters方法但是因为Map有过滤重复key的作用然后使用columnMap中记录的值就可以防止参数重复打印的问题 于是我给mybatis提了一个pr借此来修复这个问题, pr链接https://github.com/mybatis/mybatis-3/pull/3110
我的思路很简单去除columnNames和columnValues仅保留columnMap并且将columnMap改为LinkedHashMap类型以此保证参数的顺序经过测试这样做并没有发现什么问题且保证了参数的正确输出。 不过很遗憾我的pr没有被合并被关闭了对方给的理由是不保证在拦截器中做出的一些操作对mybatis的运行产生一些的副作用且给出友好提示是否有其他的方式来解决我的需求问题。
重新判断问题
我们重新思考一下这个问题问题出现在StatementHandler.getParameterHandler().setParameters 这里我对参数重新赋值会导致这个问题
那么我为何要重新赋值有没有办法不调用这个StatementHandler.getParameterHandler().setParameters 方法
我需要重新赋值的原因是因为在拦截StatementHandler的update或query方法时mybatis自身已经调用过setParameters方法
此时如果我不重新调用一下单纯的修改parameterObject 自身那么PreparedStatement.executeXXX设置的参数其实还是上次的并不会因为我修改了parameterObject 而变化
所以根据提示我们有没有办法在mybatis自身执行setParameters前进行对parameterObject 的修改这样我们在执行过程中由mybatis来做这个赋值的事情log就只会打印一次了。
最终解决
那么mybatis是何时自己进行setParameters的答案在StatementHandler.parameterize中
是的它会在update或者query方法执行前对参数进行处理所以我们应当拦截这一步的操作在这一步对参数进行处理 修改后的拦截器可以看到这里我们对StatementHandler.parameterize方法进行拦截处理并且修改参数此时log中输出的就是我们最后一次修改的参数。
当然mybatis还提供了其他的拦截点例如不拦截StatementHandler类我们直接到源头ParameterHandler.setParameters拦截设置参数方法
在这里我们修改他的参数也是可以的如下图 最终结论
可以看到我们只要不在拦截器中调用setParameters方法就不会触发log的重复打印因为mybatis的log记录类使用ArrayList记录每次的setXX入参, 因此选好时机做相应的处理就不会出现问题在合适的拦截点做相应的事情
MyBatis的参数记录可能也没有考虑过重复调用的问题或者也许有其他的考量总之我们了解这个问题的原因并且做相应的规避即可。
演示与复现问题的demo都在https://github.com/qiaomengnan16/mybatis-log-bug欢迎指正。