惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Forbes - Security
Forbes - Security
A
Arctic Wolf
M
MIT News - Artificial intelligence
T
Threat Research - Cisco Blogs
T
The Exploit Database - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
NISL@THU
NISL@THU
L
Lohrmann on Cybersecurity
Martin Fowler
Martin Fowler
A
About on SuperTechFans
P
Palo Alto Networks Blog
Project Zero
Project Zero
The GitHub Blog
The GitHub Blog
WordPress大学
WordPress大学
Blog — PlanetScale
Blog — PlanetScale
博客园_首页
大猫的无限游戏
大猫的无限游戏
Cisco Talos Blog
Cisco Talos Blog
P
Proofpoint News Feed
D
DataBreaches.Net
Cyberwarzone
Cyberwarzone
T
Tor Project blog
IT之家
IT之家
P
Proofpoint News Feed
Help Net Security
Help Net Security
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
CXSECURITY Database RSS Feed - CXSecurity.com
Microsoft Azure Blog
Microsoft Azure Blog
V2EX - 技术
V2EX - 技术
K
Kaspersky official blog
Hugging Face - Blog
Hugging Face - Blog
MongoDB | Blog
MongoDB | Blog
B
Blog
N
News and Events Feed by Topic
The Cloudflare Blog
S
Schneier on Security
P
Privacy & Cybersecurity Law Blog
T
The Blog of Author Tim Ferriss
Recorded Future
Recorded Future
Last Week in AI
Last Week in AI
The Last Watchdog
The Last Watchdog
Hacker News - Newest:
Hacker News - Newest: "LLM"
L
LangChain Blog
I
InfoQ
F
Full Disclosure
The Register - Security
The Register - Security
阮一峰的网络日志
阮一峰的网络日志
H
Hacker News: Front Page
V
V2EX

博客园 - buguge

最差实践(bad-practice):开发者在方法里直接实例化线程池对象,然后...(应用gg了) 【HttpClient最差实践(bad-practice)】开发者在 http 工具方法中直接实例化 HttpClient,然后…… Crypto、Cipher与Password:Java加密开发的三个核心概念 知识VS技能:如何优雅判空? 20260604SR超时问题排查 推敲见文章:从 `try..catch` 看异常日志打印的正确姿势 #解决问题要彻底# 慢SQL治理完成后,如何防止同类问题“死灰复燃”? 从合同甲方是荒谬的“JD”谈起:软件开发不应遗忘的“常识” 别留小尾巴/尽快剪掉小尾巴:从一次“ABA”字段重命名,谈谈“解决问题要彻底” 常见的OOM错误 ( OutOfMemoryError全类型详解) 开发者暴露了一个无需授权访问的裸接口,我问:如果有人暴力请求怎么办? 【SQL性能优化篇】有了!治理慢SQL“WHERE create_time ORDER BY id”的良药---规避“Using filesort”性能杀手 高效查询商户日终余额:一个SQL的优化实践 Hutool 的 `TimedCache` 到期会自动清理吗? ——————hutool cache的"惰性清理"和"定期清理" Fastjson枚举反序列化:当字符串不是枚举常量名时,会发生什么? fastjson-EnumDeserializer类及源码分析 `UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only` 异常解析 认识2个单词:goal/target —————— 为什么Maven是 "goal" 而不是 "target"? 聚合系统设计:策略模式(Strategy Pattern)在银行通道对接场景中的应用 这样构建对象,太帅了!—— 阶梯式Builder模式与代码整洁之道 注意!字段数据类型不匹配,这个sql会很慢 还在用ArrayList?用HashSet吧!--性能对比 分页查询还在用create_time去做降序? 从 synchronized 到 ConcurrentHashMap:一个小小的并发控制策略升级优化,证明我还是初级程序员 面试题沟通整理 【研发笔记20260120】值得记录:靠谱程序员的回聘 研发笔记:如何消除长字符串的秘钥数据对RPC负荷、日志量、系统安全所带来的伤害? 在java中实现c#的int.TryParse方法 灵活用工平台-连续劳务所得税-计算器-工具类,拿走不谢 Apollo场景建议配置指南:充分发挥分布式配置中心优势
未给entity的主属性赋值,Mybatisplus却抛出了type mismatch异常。——————分享一下Mybatisplus主键填充机制
buguge · 2025-12-24 · via 博客园 - buguge

📚 问题介绍

今天在客户联调时,程序出现一个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赋值时,就会出现类型转换异常。

image

那么,为什么 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 时,事件的完整流程如下:

  1. 策略触发:MyBatis-Plus 检测到主键字段 orderNo 的值为 null
  2. 策略选择:由于orderNo字段的 @TableId 注解未显式指定 type,MyBatis-Plus 需要确定使用哪种策略来生成主键值。
  3. 值生成:系统根据确定的策略(在这里是 ID_WORKER)生成了一个 Long 类型的数字(如 2003347475481600001)。
  4. 赋值失败:MyBatis-Plus 尝试通过反射将这个 Long 型 的值设置到您实体类中定义的 String 类型 的 orderNo 字段上。由于 Java 是强类型语言,Long 无法直接赋给 String,因此在反射调用 setOrderNo 方法时抛出了 IllegalArgumentException: argument type mismatch。这个异常最终被包装成了 ReflectionException

🔍 深入探索:为什么默认策略是 ID_WORKER?

当未指定 @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 的习惯。这可以避免因依赖全局默认配置(可能被不同人无意中修改)而导致的意外错误,使代码更加健壮和可读。