You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用tk.mybatis.mapper偶发OGNL属性不存在异常求助

偶发OGNL NoSuchPropertyException排查与解决

根据你描述的场景——使用tk.mybatis.mapper 3.3.0的updateByExampleSelective偶发抛出NoSuchPropertyException,结合异常堆栈和你的代码,我整理了几个实际生产中遇到过的排查方向:

1. 先核对对象类型与属性名的拼写差异

仔细看异常信息:

org.apache.ibatis.ognl.NoSuchPropertyException: com.xxx.xxx.batch.model.pojo.XXXXXXXInfo.masFlag

你提到实体类是EntiyObject且有magFlag属性,但异常里找的是XXXXXXXInfo类的masFlag(注意mag和mas的拼写差异)!这很可能是偶发场景下,你传入的entiyObject被错误替换成了XXXXXXXInfo对象——比如线程池里的对象复用、请求参数串线,导致Mapper拿到了错误的实体类,自然找不到对应的属性。

建议在调用updateByExampleSelective前加一行日志,打印entiyObject.getClass().getName()和属性值,确认偶发错误时的对象类型是否正确。

2. 升级tk.mybatis版本解决并发bug

tk.mybatis 3.3.0是2017年的老版本,早期的动态SQL生成逻辑存在线程安全问题,尤其是在多线程批量执行场景下(你的日志显示是线程池线程pool-9-thread-5)。OGNL的属性映射缓存在并发时可能出现混乱,导致偶尔找不到属性。

建议直接升级到tk.mybatis的最新稳定版(比如3.5.4+),官方在后续版本中修复了大量并发场景下的动态SQL生成问题,这是最直接的解决方案。

3. 排查类加载器不一致问题

在Spring多线程或模块化环境中,偶尔会出现类加载器不一致的情况:比如EntiyObject被不同的类加载器加载,导致OGNL认为是两个不同的类,从而找不到属性。

可以在异常捕获时添加日志,打印:

System.out.println("实体类加载器:" + entiyObject.getClass().getClassLoader());
System.out.println("异常中类加载器:" + com.xxx.xxx.batch.model.pojo.XXXXXXXInfo.class.getClassLoader());

如果两个类加载器不一致,需要排查Spring的类加载配置,比如是否使用了自定义类加载器,或者线程池的上下文类加载器是否被修改。

4. 临时禁用OGNL缓存验证问题

MyBatis的OGNL缓存可能在并发场景下缓存了错误的属性信息,可以临时禁用OGNL缓存来验证:
在MyBatis的mybatis-config.xml中添加:

<settings>
    <!-- 禁用OGNL缓存 -->
    <setting name="ognlCacheEnabled" value="false"/>
</settings>

如果禁用后问题消失,就可以确认是OGNL缓存的并发问题,升级tk.mybatis版本即可彻底解决。

5. 处理Spring代理导致的类信息丢失

你的Service被Spring CGLIB代理(日志中有EnhancerBySpringCGLIB),如果实体类在传递过程中被意外代理,可能导致OGNL无法识别属性。可以在调用Mapper前,将代理对象转为原始对象:

// 工具方法:获取Spring代理的原始对象
private static <T> T getOriginalObject(Object proxy) throws Exception {
    if (!AopUtils.isAopProxy(proxy)) {
        return (T) proxy;
    }
    if (AopUtils.isJdkDynamicProxy(proxy)) {
        Field hField = proxy.getClass().getSuperclass().getDeclaredField("h");
        hField.setAccessible(true);
        AopProxy aopProxy = (AopProxy) hField.get(proxy);
        Field advisedField = aopProxy.getClass().getDeclaredField("advised");
        advisedField.setAccessible(true);
        AdvisedSupport advised = (AdvisedSupport) advisedField.get(aopProxy);
        return (T) advised.getTargetSource().getTarget();
    } else { // CGLIB代理
        Field field = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
        field.setAccessible(true);
        Object dynamicAdvisedInterceptor = field.get(proxy);
        Field advisedField = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
        advisedField.setAccessible(true);
        AdvisedSupport advised = (AdvisedSupport) advisedField.get(dynamicAdvisedInterceptor);
        return (T) advised.getTargetSource().getTarget();
    }
}

// 调用时
EntiyObject realEntity = getOriginalObject(entiyObject);
entiyObjectMapper.updateByExampleSelective(realEntity, example);

内容的提问来源于stack exchange,提问作者Devean

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:33:37