






















今天在客户联调时,程序出现一个ReflectionException: argument type mismatch 异常。
通过分析异常堆栈及代码得知,在使用Mybatisplus的com.baomidou.mybatisplus.core.mapper.BaseMapper#insert保存LevyAccountRecharge数据时,当程序未给主键字段“orderNo”赋值时,就会抛出来这个异常。
具体异常message是:Could not set property 'orderNo' of 'class com.emax.trans.provider.modules.accountrecharge.entity.LevyAccountRecharge' with value '2003347475481600001' Cause: java.lang.IllegalArgumentException: argument type mismatch。 ————即:无法将Long数字赋值给LevyAccountRecharge的“orderNo”属性。
LevyAccountRecharge 实体类定义如下,其中字符串属性 orderNo 上有注解@TableId。orderNo对应mysql数据表varchar(32)的主键字段。
@Data
@TableName(value = "levy_account_recharge",autoResultMap = true)
public class LevyAccountRecharge {
/**业务订单号,主键*/
@TableId
private String orderNo;
}
下面贴出来详细的异常stacktrace:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Could not set property 'orderNo' of 'class com.emax.trans.provider.modules.accountrecharge.entity.LevyAccountRecharge' with value '2003347475481600001' Cause: java.lang.IllegalArgumentException: argument type mismatch
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
at com.sun.proxy.$Proxy206.insert(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:278)
at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:58)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:62)
at com.sun.proxy.$Proxy207.insert(Unknown Source)
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.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy208.insert(Unknown Source)
at com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.save(ServiceImpl.java:104)
at com.emax.trans.provider.modules.accountrecharge.service.LevyAccountRechargeManager.save(LevyAccountRechargeManager.java:200)
...
Caused by: org.apache.ibatis.reflection.ReflectionException: Could not set property 'orderNo' of 'class com.emax.trans.provider.modules.accountrecharge.entity.LevyAccountRecharge' with value '2003347475481600001' Cause: java.lang.IllegalArgumentException: argument type mismatch
at org.apache.ibatis.reflection.wrapper.BeanWrapper.setBeanProperty(BeanWrapper.java:185)
at org.apache.ibatis.reflection.wrapper.BeanWrapper.set(BeanWrapper.java:59)
at org.apache.ibatis.reflection.MetaObject.setValue(MetaObject.java:140)
at com.baomidou.mybatisplus.core.MybatisDefaultParameterHandler.populateKeys(MybatisDefaultParameterHandler.java:181)
at com.baomidou.mybatisplus.core.MybatisDefaultParameterHandler.processBatch(MybatisDefaultParameterHandler.java:123)
at com.baomidou.mybatisplus.core.MybatisDefaultParameterHandler.<init>(MybatisDefaultParameterHandler.java:52)
at com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver.createParameterHandler(MybatisXMLLanguageDriver.java:34)
at com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver.createParameterHandler(MybatisXMLLanguageDriver.java:28)
at org.apache.ibatis.session.Configuration.newParameterHandler(Configuration.java:563)
at org.apache.ibatis.executor.statement.BaseStatementHandler.<init>(BaseStatementHandler.java:69)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.<init>(PreparedStatementHandler.java:41)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.<init>(RoutingStatementHandler.java:46)
at org.apache.ibatis.session.Configuration.newStatementHandler(Configuration.java:576)
at com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor.doUpdate(MybatisSimpleExecutor.java:52)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)
...
Caused by: java.lang.IllegalArgumentException: argument type mismatch
...
用下面的junit测试代码,可以再现出来这个异常:
@Test
public void save() {
LevyAccountRecharge byId = levyAccountRechargeManager.getById("2025112319260202940444");
byId.setOrderNo(null);
levyAccountRechargeManager.save(byId);
}
我的疑问是:为什么 orderNo 是null 时,Mybatisplus会抛出这个异常,而不是类似“不能将null值插入主键字段”的异常呢?
于是,立即开启排障之旅。
我注意到异常stacktrace里有MybatisDefaultParameterHandler,于是加上断点进行debug。结果惊奇地发现,其idType 竟然是 ID_WORKER。显然,由于 ID_WORKER 会生成Long数值,在给String赋值时,就会出现类型转换异常。

那么,为什么 LevyAccountRecharge.orderNo 的IdType是默认的IdType.NONE,而程序跑出来的却是 IdType.ID_WORKER 呢?
为了简单,我直接使用 TableInfo tableInfo = TableInfoHelper.getTableInfo(LevyAccountRecharge.class);来看看,发现其IdType也是 IdType.ID_WORKER 。
为什么是ID_WORKER呢?为什么是ID_WORKER呢?为什么是ID_WORKER呢?
上面遇到的 ReflectionException: argument type mismatch 异常,根本原因在于 MyBatis-Plus 的主键自动填充机制与实体类字段类型不匹配。
当执行 save 操作且 orderNo 为 null 时,事件的完整流程如下:
orderNo 的值为 null。@TableId 注解未显式指定 type,MyBatis-Plus 需要确定使用哪种策略来生成主键值。ID_WORKER)生成了一个 Long 类型的数字(如 2003347475481600001)。orderNo 字段上。由于 Java 是强类型语言,Long 无法直接赋给 String,因此在反射调用 setOrderNo 方法时抛出了 IllegalArgumentException: argument type mismatch。这个异常最终被包装成了 ReflectionException。当未指定 @TableId 的 IdType 时,其默认值是 IdType.NONE,其javadoc明确指出:表示未设置主键类型(将跟随全局)。
注意其中的4个大字“跟随全局”。IdType.NONE 的含义是“未设置”,其行为就是回退到使用全局配置的策略。
我们来了解一下Mybatisplus的主键填充机制。这背后的逻辑在于 MyBatis-Plus 的 策略查找链。优先级从高到低如下:
ID_WORKER策略是使用雪花算法生成 Long 类型 的全局唯一 ID。 这就是为什么注解上的 @TableId(等价于 @TableId(type = IdType.NONE))运行时实际生效的策略是 ID_WORKER 的原因。
显然,使用 ID_WORKER_STR 策略 来确保主键生成策略与实体字段类型、数据库字段类型三者一致。
将注解修改为 @TableId(type = IdType.ID_WORKER_STR)。这个策略专为字符串类型设计,它同样基于雪花算法,但生成的是 String 类型 的 ID,完美匹配您的 varchar(32) 数据库字段和 String orderNo。
@Data
@TableName(value = "levy_account_recharge", autoResultMap = true)
public class LevyAccountRecharge {
/** 业务订单号,主键 */
@TableId(type = IdType.ID_WORKER_STR) // 明确指定使用字符串版本的雪花算法
private String orderNo;
// ... 其他字段
}
orderNo 字段为 String 类型,但因其主键策略未显式设置,MyBatis-Plus 使用了默认的 ID_WORKER 策略来生成 Long 类型的值,导致类型不匹配的赋值异常。@TableId 注解中显式指定 type = IdType.ID_WORKER_STR。@TableId 的 type 的习惯。这可以避免因依赖全局默认配置(可能被不同人无意中修改)而导致的意外错误,使代码更加健壮和可读。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。