项目用的 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. bootstrap 模态框(modal)插件使用

    今天用户登陆时,在原网页上弹出新登陆窗口,发现使用的是modal插件,记录下该插件的使用方法,手写强化下. 首先,模态框(modal)是覆盖在父窗体上的子窗体,目的是显示来自一个单独的源的内容,可以在 ...

  2. 基于Multiple treatment的营销评估算法

    营销是发现或挖掘准消费者和众多商家需求,通过对自身商品和服务的优化和定制,进而推广.传播和销售产品,实现最大化利益的过程.例如,银行可通过免息卡或降价对处在分期意愿边缘的用户进行营销,促使其分期进而提 ...

  3. [BZOJ 4417][Shoi2013]超级跳马

    4417: [Shoi2013]超级跳马 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 379  Solved: 230[Submit][Status ...

  4. C++多线程学习之(一)——并发与多线程

    1 并发 计算机领域的并发指的是在单个系统里同时执行多个独立的任务,而非顺序地进行一些活动. 1.1 并发的途径 多进程并发:将应用程序分为多个独立的进程,它们在同一时刻运行,就像同时进行网页浏览和文 ...

  5. C++之异常捕获和处理

    一.简介   在C++语言中,异常处理包括:throw表达式,try语句块,一套异常类.其中,异常类用于在throw表达式和相关的catch子句之间传递异常的具体信息.exception头文件定义了最 ...

  6. Week03-面向对象入门

    1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词,如类.对象.封装等 类 对象 封装 继承 覆盖 重载 构造函数 static public private toString f ...

  7. 《高级软件测试》11.14.安装和运行Jira

    今日任务完成情况如下: 小段:研究Jira在Linux的安装教程 小费:尝试在Ubuntu下安装Jira 小高:查阅了关于Jira软件的介绍和安装教程,下载准备明天安装,并学习使用 小王:注册Jira ...

  8. 第三篇:Python字符编码

    一 .了解字符编码的知识储备 1计算机基础知识 1.2文本编辑器存取文件的原理(nodepat++,Pycharm,word) #.打开编辑器就打开了启动了一个进程,是在内存中的,所以,用编辑器编写的 ...

  9. Python爬虫基本原理

    爬虫基本原理 1. 什么是爬虫 请求网站并提取数据的自动化程序. 2. 爬虫基本流程 发起请求 通过HTTP库向目标站点发起请求,即发送一个Request,请求可以包含额外的headers等信息,等待 ...

  10. ajax的原理解析

    一.关于同步与异步的分析: 异步传输是面向字符的传输,它的单位是字符:而同步传输是面向比特的传输,它的单位是桢,它传输的时候要求接受方和发送方的时钟是保持一致的.而ajax就是采用的异步请求方式的. ...