当前使用版本(必填,否则不予处理)
3.5.3.1
该问题是如何引起的?(确定最新版也有问题再提!!!)
DataChangeRecorderInnerInterceptor.dealOperationResult 调用接口,saveBatch 多条插入 导致 只有一条变更记录 实际插入数据库是多条
重现步骤(如果有就写完整)
报错信息
代码打印日志结果与预期不一致
Comment From: yuxiaobin
目前saveBatch是调用BatchExecutor.doUpdate() 这里stmt是缓存的,不会调用prepare,而目前插件是拦截在prepare 如果改成Executor.update 会出现主键未赋值的情况:即拿到的变更记录里没有主键
建议:不是很大的数据量,直接foreach save
Comment From: farmer-youngest
我把 beforePrepare代码放到 willDoUpdate 就可以获取所有的savebatch
`public class DataLogRecorderInnerInterceptor extends DataChangeRecorderInnerInterceptor {
@Autowired
DataLogMapper dataLogMapper;
private static final ThreadLocal<String> sql = new ThreadLocal<>();
private static String DATA_LOG_TABLE_NAME = "CP_DATA_LOG";
public DataLogRecorderInnerInterceptor(@Autowired
@Qualifier("appBatisPlusInterceptor") MybatisPlusInterceptor appBatisPlusInterceptor) {
appBatisPlusInterceptor.addInnerInterceptor(this);
}
@Override
public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
try{
willLogBeforeDoUpdate(executor, ms, parameter);
}catch (Exception e){
logger.error("记录日志 err",e);
}
return true;
}
@Override
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
//do nothing
}
/**
* 处理batch操作 只记录一个log问题
* @param executor
* @param ms
* @param parameter
* @throws SQLException
*/
protected void willLogBeforeDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
SqlCommandType sct = ms.getSqlCommandType();
String sqlSimple = boundSql.getSql();
Connection connection = executor.getTransaction().getConnection();
if ((sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE)&&!StrUtil.containsAnyIgnoreCase(sqlSimple,DATA_LOG_TABLE_NAME)) {
PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
OperationResult operationResult;
long startTs = System.currentTimeMillis();
try {
Statement statement = CCJSqlParserUtil.parse(mpBs.sql());
if (statement instanceof Insert) {
operationResult = processInsert((Insert) statement, boundSql);
} else if (statement instanceof Update) {
operationResult = processUpdate((Update) statement, ms, boundSql, connection);
} else if (statement instanceof Delete) {
operationResult = processDelete((Delete) statement, ms, boundSql, connection);
} else {
logger.info("other operation sql={}", mpBs.sql());
return;
}
} catch (Exception e) {
logger.error("Unexpected error for mappedStatement={}, sql={}", ms.getId(), mpBs.sql(), e);
return;
}
long costThis = System.currentTimeMillis() - startTs;
if (operationResult != null ) {
sql.set(sqlSimple);
operationResult.setCost(costThis);
dealOperationResult(operationResult);
}
}
}
@Override
protected void dealOperationResult(OperationResult operationResult) {
try{
DataLogEntity dataLogEntity = new DataLogEntity();
dataLogEntity.setOperatorId(Optional.of(SecurityUtils.getUserId()).orElse(0L));
dataLogEntity.setOperatorName(Optional.ofNullable(SecurityUtils.getUsername()).orElse(""));
dataLogEntity.setCreateTime(LocalDateTime.now());
String[] classAndMethod = DataLogUtils.getClassAndMethod();
dataLogEntity.setClassName(classAndMethod[0]);
dataLogEntity.setMethodName(classAndMethod[1]);
dataLogEntity.setStatement(sql.get());
dataLogEntity.setTableName(operationResult.getTableName());
dataLogEntity.setChangedData(operationResult.getChangedData());
dataLogEntity.setCost(operationResult.getCost());
dataLogEntity.setOperationType(operationResult.getOperation());
// 写日志
if (!StrUtil.equalsIgnoreCase(DATA_LOG_TABLE_NAME, operationResult.getTableName())) {
dataLogMapper.insert(dataLogEntity);
}
}finally {
sql.remove();
}
}
} `
Comment From: farmer-youngest
不知道我这个写法会有什么问题 ,作者为什么最开始用的 beforePrepare 回调函数
Comment From: farmer-youngest
目前saveBatch是调用BatchExecutor.doUpdate() 这里stmt是缓存的,不会调用prepare,而目前插件是拦截在prepare 如果改成Executor.update 会出现主键未赋值的情况:即拿到的变更记录里没有主键
建议:不是很大的数据量,直接foreach save
求解
Comment From: lunxian8
目前saveBatch是调用BatchExecutor.doUpdate() 这里stmt是缓存的,不会调用prepare,而目前插件是拦截在prepare 如果改成Executor.update 会出现主键未赋值的情况:即拿到的变更记录里没有主键
建议:不是很大的数据量,直接foreach save
我是mp3.4版本的 使用分表也是这样只进去一次 难道也是这样stmt缓存吗? 目前是按照字段分组 然后saveBatch 或者其它Batch方法
Comment From: SayHello-SayBye
版本:3.5.3 情况:使用saveBatch批量保存,operationResult.getOperation() = insert的情况下operationResult.getChangedData()只有单条记录 解决方案:saveBatch修改为foreach save