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将异常捕获的问题,导致不好排查。