当前使用版本(必填,否则不予处理)

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