项目用的 Mybatis,今天改一个需求,落地实现是批量更新,且只需要根据主键(id)来更新一个字段(name)。

于是,没有犹豫,像下面这样设计了数据结构:

  • 既然是批量更新,那外层肯定是 List
  • List 中每个元素,只包含 id & name,于是,选择了用 org.apache.commons.lang3.tuple.Pair 来封装数据(就是不想自己再写一个 DO 或者 VO 或者 MO)
  • 最终的数据结构是:List<Pair<Integer, String>>

XML 中的 Sql 语句,很简单,如下:

    <update id="updateByApacheCommonsPair">
<foreach collection="list" separator=";" item="x">
UPDATE employee SET name = #{x.right} WHERE id = #{x.left}
</foreach>
</update>

提示:XML 中的 Sql 引入了一个名为 x 的变量(后面会用到)

一切顺利,但测试时,报错如下:

org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'right' in 'class java.lang.String'
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: UPDATE employee SET name = ? WHERE id = ?
### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'right' in 'class java.lang.String' at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:200)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:62)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53)
at com.sun.proxy.$Proxy4.updateByApacheCommonsPair(Unknown Source)
at com.atguigu.mybatis.test.MyBatisTest.testUpdateByApacheCommonsPair(MyBatisTest.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'right' in 'class java.lang.String'
at org.apache.ibatis.reflection.Reflector.getGetInvoker(Reflector.java:409)
at org.apache.ibatis.reflection.MetaClass.getGetInvoker(MetaClass.java:164)
at org.apache.ibatis.reflection.wrapper.BeanWrapper.getBeanProperty(BeanWrapper.java:162)
at org.apache.ibatis.reflection.wrapper.BeanWrapper.get(BeanWrapper.java:49)
at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:122)
at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:119)
at org.apache.ibatis.mapping.BoundSql.getAdditionalParameter(BoundSql.java:75)
at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:72)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:93)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:64)
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:86)
at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:198)
... 26 more

问题已经出了,那就解决吧。

报错中的提示信息还是比较容易阅读:java.lang.String 类中没有 right 字段的 getter 方法

奇怪,怎么会报这样的错:

  • java.lang.String 类中确实没有 right 字段
  • right 字段是 Pair 中的(因为我用的就是 Pair)
  • 它们两者搅在一起了?

猜测了一些原因,不过都不对,最后,没办法了,只有自己写了一个 CYHPair (也是一个包含两个字段的POJO)来实现,结果,又是可以的。

所以,接下来就有思路了:比较自己写的 CYHPair 和 org.apache.commons.lang3.tuple.Pair 在哪些地方有所不同。

最后发现(实验了很长时间),一个比较大的不同之处在于,org.apache.commons.lang3.tuple.Pair 实现了 Map.Entry 接口(自己写的 CYHPair 没有),这会不会有影响?

最终的结论证明:就是因为实现了 Map.Entry 所以导致了后面取不到值,所以报错如上。

可是,这又是为什么呢?于是,继续源码调试。

Demo 代码地址:https://github.com/cyhbyw/mybatis_atguigu

Demo 工程名称:MyBatis_CYH_test_MapEntry

第一:先来调试正常的Case(即基于 CYHPair 来做更新操作的)

01. 可以看到,Line#73 判断了是否属于 Map.Entry;由于我的 CYHPair 没有实现它,所以,代码进入 else 部分;注意这里的第二个参数是 o 本身,而 o 是 CYHPair 的一个实例

02. 从 Line#102 可以看到,将 o 这个  CYHPair 对象绑定给了 x (提示:x 是 Sql 中的变量)

简单结论:x 这个变量是 CYHPair 类型的!

第二步:接下来调试不正常的(即基于 org.apache.commons.lang3.tuple.Pair 来做更新操作的)

01. 还是 Line#73 行的判断,不过这一次,判断成立,于是,代码走到 Line#77 行;注意第二个参数是 mapEntry.getValue(),而这个值是字符串 ApacheCommonsPair;

02. 绑定的时候,就将 ApacheCommonsPair 这个字符串值绑定给了变量 x

03. 所以现在容易理解它为什么会报错 “java.lang.String 类中没有 right 字段的 getter 方法” 了,因为就是 02 步中将字符串 ApacheCommonsPair 绑定给了 x 呀,那就肯定没有 right 字段也没有对应的 getter 方法啦~~

org.apache.commons.lang3.tuple.Pair 作为更新参数,XML 中的 Sql 取不到值、报错的更多相关文章

  1. 【java】org.apache.commons.lang3功能示例

    org.apache.commons.lang3功能示例 package com.simple.test; import java.util.Date; import java.util.Iterat ...

  2. org.apache.commons.lang3.ArrayUtils 学习笔记

    package com.nihaorz.model; /** * @作者 王睿 * @时间 2016-5-17 上午10:05:17 * */ public class Person { privat ...

  3. spring异常记录-----java.lang.NoClassDefFoundError: org/apache/commons/lang3/StringUtils

    今天在练习怎样SSH中进行单元測试的时候出现下列异常: SEVERE: Exception starting filter Struts2 java.lang.NoClassDefFoundError ...

  4. Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang3.StringUtils

    1.错误叙述性说明 2014-7-10 23:12:23 org.apache.catalina.core.StandardContext filterStart 严重: Exception star ...

  5. org.apache.commons.lang3 的随机数生成

    apache org.apache.commons.lang3 的随机数生成工具,方便使用. String a12 = RandomStringUtils.random(4, "012345 ...

  6. NoClassDefFoundError: org/apache/commons/lang3/StringUtils

    出错信息: 2014-2-5 21:38:05 org.apache.catalina.core.StandardContext filterStart严重: Exception starting f ...

  7. Hadoop java.lang.ClassNotFoundException: org.apache.commons.lang3.StringUtils

    .jar 学习好友推荐案例的时候,提交运行时报错找不到StringUtils java.lang.ClassNotFoundException: org.apache.commons.lang3.St ...

  8. 20190313 org.apache.commons.lang3.builder.EqualsBuilder的两种典型用法

    org.apache.commons.lang3.builder.EqualsBuilder的两种典型用法 public boolean equals(Object obj) { if (obj == ...

  9. struts2中的错误--java.lang.NoClassDefFoundError: org/apache/commons/lang3/StringUtils

    2013-4-7 10:13:56 org.apache.catalina.startup.HostConfig checkResources 信息: Reloading context [/chap ...

随机推荐

  1. 爬虫(scrapy第一篇)

    ---------------------------------------------------------------------------------------------------- ...

  2. canvas填充样式

    填充样式主要针对fillStyle.fillStyle除了可以赋值为color,还可以赋值渐变色,包括线性渐变色和径向渐变色,还是和css3里的内容类似. 一.线性渐变 1.设置线性渐变的填充样式 设 ...

  3. Could not create pool connection. The DBMS driver exception was: null, message from server: "Host '192.168.XX.XX' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'

    早上打开浏览器准备登陆某个系统,发现Error 404--Not Found,有点奇怪,这个服务器应该没人用了才对,然后到weblogic后台去看日志,报如下错误: "Could not c ...

  4. selenium 爬虫

    from selenium import webdriver import time driver = webdriver.PhantomJS(executable_path="D:/pha ...

  5. memmove 和 memcpy的区别以及处理内存重叠问题

    区别: memcpy和memmove()都是C语言中的库函数,在头文件string.h中,作用是拷贝一定长度的内存的内容,原型分别如下: void *memcpy(void *dst, const v ...

  6. Scala 对象

    1. 单例对象 对于任何你在Java中会使用单例对象的地方, 在scala中都可以使用对象来实现; scala字段没有静态方法或者静态字段, 可以使用object语法结构达到同样的效果,对象(obje ...

  7. jquery基础总结 -- 转载

    jquery的each里面return的使用 在使用jquery的each方法时, return false相当于break,是跳出each循环,return true相当于continue,是继续循 ...

  8. hexo博客图片问题

    hexo博客图片问题 第一步 首先确认_config.yml 中有 post_asset_folder:true. Hexo 提供了一种更方便管理 Asset 的设定:post_asset_folde ...

  9. JAVA_SE基础——39.继承

    在面向对象程序设计中,可以从已有的类派生出新类. 这称做继承(inheritance). 白话解释: 例子1:继承一般是指晚辈从父辈那里继承财产,也可以说是子女拥有父母给予他们的东西. 例子2:猫和狗 ...

  10. NoSQL简介

    相信大家也多多少少了解过一些数据库,最常用的当属MySQL了,当然也这是关系型数据库的代表了 常见的关系型数据库有:MySQL.SQLServer.Oracle 而数据库也有另一个流派-----NoS ...