mybatisplus的executeBatch方法报空指针问题分析

1.报错日志

java.lang.NullPointerException: null
        at java.base/java.util.Objects.requireNonNull(Objects.java:221)
        at com.baomidou.mybatisplus.extension.toolkit.SqlHelper.executeBatch(SqlHelper.java:189)
        at com.baomidou.mybatisplus.extension.toolkit.SqlHelper.executeBatch(SqlHelper.java:211)
        at com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.executeBatch(ServiceImpl.java:239)
        at com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.saveBatch(ServiceImpl.java:135)
        at com.baomidou.mybatisplus.extension.service.IService.saveBatch(IService.java:73)
        at com.rainstorm.core.mysql.BaseDaoServiceImpl.saveBatch(BaseDaoServiceImpl.java:80)
        at com.rainstorm.core.mysql.BaseDaoServiceImpl$$FastClassBySpringCGLIB$$3473b65e.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
        at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
        at com.rainstorm.core.mysql.device.dao.impl.DeviceMeterEnergyRecordDao$$EnhancerBySpringCGLIB$$368f097b.saveBatch(<generated>)
        at com.rainstorm.service.device.impl.DeviceDpReportServiceImpl.meterReading(DeviceDpReportServiceImpl.java:87)
        at com.rainstorm.service.pulsar.handler.impl.wrapper.PanelMeterReadingWrapper.doHandle(PanelMeterReadingWrapper.java:50)
        at com.rainstorm.service.pulsar.handler.impl.wrapper.AbstractPulsarHandlerWrapper.handleMessage(AbstractPulsarHandlerWrapper.java:59)
        at com.rainstorm.service.pulsar.handler.support.PulsarHandlerChain.handleMessage(PulsarHandlerChain.java:90)
        at com.rainstorm.service.pulsar.PulsarConsumer$1.onMessageArrived(PulsarConsumer.java:71)
        at com.rainstorm.service.pulsar.PulsarConsumer$1.onMessageArrived(PulsarConsumer.java:58)
        at com.aladdin.message.sdk.pulsar.PulsarMultiTenantsService$InnerMessageConsumer.onMessageArrived(PulsarMultiTenantsService.kt:82)
        at com.aladdin.message.sdk.pulsar.PulsarMultiTenantsService$InnerMessageConsumer.onMessageArrived(PulsarMultiTenantsService.kt:73)
        at com.aladdin.message.sdk.pulsar.PulsarConsumer.messageArrive(PulsarConsumer.kt:142)
        at com.aladdin.message.sdk.pulsar.PulsarConsumer.access$messageArrive(PulsarConsumer.kt:27)
        at com.aladdin.message.sdk.pulsar.PulsarConsumer$startSubscribeLoopByEnv$1.run(PulsarConsumer.kt:101)

2.分析原因

2.1排除问题过程:

(1)看到上述报错,以为是bean未注入导致的空指针异常,但实际排除发现bean注入是正常的

(2)查看报错代码位置
SqlHelper.java:211


    /**
     * 执行批量操作
     *
     * @param entityClass 实体类
     * @param log         日志对象
     * @param list        数据集合
     * @param batchSize   批次大小
     * @param consumer    consumer
     * @param <E>         T
     * @return 操作结果
     * @since 3.4.0
     */
    public static <E> boolean executeBatch(Class<?> entityClass, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
        Assert.isFalse(batchSize < 1, "batchSize must not be less than one");
        return !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, sqlSession -> {
            int size = list.size();
            int i = 1;
            for (E element : list) {
 211行               consumer.accept(sqlSession, element);
                if ((i % batchSize == 0) || i == size) {
                    sqlSession.flushStatements();
                }
                i++;
            }
        });
    }

SqlHelper.java:189


    /**
     * 执行批量操作
     *
     * @param entityClass 实体
     * @param log         日志对象
     * @param consumer    consumer
     * @return 操作结果
     * @since 3.4.0
     */
    public static boolean executeBatch(Class<?> entityClass, Log log, Consumer<SqlSession> consumer) {
        SqlSessionFactory sqlSessionFactory = sqlSessionFactory(entityClass);
        SqlSessionHolder sqlSessionHolder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory);
        boolean transaction = TransactionSynchronizationManager.isSynchronizationActive();
        if (sqlSessionHolder != null) {
            SqlSession sqlSession = sqlSessionHolder.getSqlSession();
            //原生无法支持执行器切换,当存在批量操作时,会嵌套两个session的,优先commit上一个session
            //按道理来说,这里的值应该一直为false。
            sqlSession.commit(!transaction);
        }
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        if (!transaction) {
            log.warn("SqlSession [" + sqlSession + "] was not registered for synchronization because DataSource is not transactional");
        }
        try {
            consumer.accept(sqlSession);
            //非事物情况下,强制commit。
            sqlSession.commit(!transaction);
            return true;
        } catch (Throwable t) {
            sqlSession.rollback();
            Throwable unwrapped = ExceptionUtil.unwrapThrowable(t);
            if (unwrapped instanceof RuntimeException) {
                MyBatisExceptionTranslator myBatisExceptionTranslator
                    = new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true);
                throw Objects.requireNonNull(myBatisExceptionTranslator.translateExceptionIfPossible((RuntimeException) unwrapped));
            }
            throw ExceptionUtils.mpe(unwrapped);
        } finally {
189行            sqlSession.close();
        }
    }

看到这也是云里雾里,但是看到了
throw Objects.requireNonNull(myBatisExceptionTranslator.translateExceptionIfPossible((RuntimeException) unwrapped));
这行代码,与抛出的空指针异常一致,但具体的异常是什么,由于在执行consumer.accept(sqlSession);时将异常catch了,但没有给出对应的错误日志,所以实际上是底层报错被mybatisplus给吞掉了,所以不好排查

(3)查看插入数据库的数据结构

{"balance":"","category":1,"deviceId":"vdevo168429265486606","meterId":"111222333","readType":1,"recordDay":18,"recordMonth":5,"recordYear":2023,"uid":"ay16837987960938dBCY"}

对比数据库相关字段,发现balance为decimal类型
balance decimal(20,4) NOT NULL DEFAULT ‘0.0000’ COMMENT ‘余额’,

所以初步怀疑是数据类型不匹配的问题。

(4)将插入数据库的数据修改为
{“balance”:“0.01”,“category”:1,“deviceId”:“vdevo168429265486606”,“meterId”:“111222333”,“readType”:1,“recordDay”:18,“recordMonth”:5,“recordYear”:2023,“uid”:“ay16837987960938dBCY”}

发现数据可以正常入库。

3.结论

实际上该异常是由数据类型比匹配,或者数据超长导致的,由于mybatis将异常捕获的问题,导致不好排查。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值