以Mybatis-plus批量新增引发的思考

前言

首先我们常用的方法一般是saveOrUpdateBatch(),源码长这样:
serviceImpl基类的入口
sqlHelper里面的实现
下面看看executeBatch怎么在处理数据
executeBatch外层包装
executeBatch实际底层执行
可以简单看出来他确实是一个for循环,只不过到指定次数进行一次flush,减少了sqlsession.flushStatements()的耗时。sql日志打印,也确实是一条条出现的。
那么有没有一种真正的批量操作呢?

发现

经过网上一番查找,发现mp本身提供批量功能,但是为了兼容多种数据库的考量,选择在扩展功能引入真正的批量方法。
引用一下原作者的说法

新实践

导入扩展包
		<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-extension</artifactId>
            <version>3.5.2</version>
        </dependency>
定义一个自己的拦截器
public class MyInjector extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
        //增加自己想要的方法, 必须是AbstractMethod的子类
        methodList.add(new InsertBatchSomeColumn());//InsertBatchSomeColumn就是扩展包里
        return methodList;
    }
}
注入拦截器
@Configuration
@Slf4j
@MapperScan("com.rikka.sqlgenerator.mapper") //注意这里扫描路径
public class MybatisPlusConfig {
    /**
     * 注入拦截器
     * @return
     */
    @Bean
    public MyInjector myInjector() {
        return new MyInjector();
    }
}
新增一个自己的mapper
@Configuration
@Slf4j
@MapperScan("com.rikka.sqlgenerator.mapper")
public class MybatisPlusConfig {
    /**
     * 注入拦截器
     * @return
     */
    @Bean
    public MyInjector myInjector() {
        return new MyInjector();
    }

}
让原mapper改为继承RootMapper
public interface SysSequenceMapper extends RootMapper<SysSequence> {

}
编写测试类
@SpringBootTest
public class BatchMethodTest {
    @Resource
    private SysSequenceMapper sysSequenceMapper;

    /**
     * 测试批量方法
     */
    @Test
    @Transactional(rollbackFor = Exception.class) //测试类会让数据库回滚
    // @Rollback(value = false)
    public void testBatchMethod() {
        ArrayList<SysSequence> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            SysSequence sysSequence = new SysSequence();
            sysSequence.setSeqName("t_test" + i);
            sysSequence.setCurrentVal(1);
            sysSequence.setInitVal(1);
            sysSequence.setIncrementVal(1);
            sysSequence.setMaxVal(99999999);
            sysSequence.setMinVal(1);
            sysSequence.setCreateTime(LocalDateTime.now());
            sysSequence.setUpdateTime(LocalDateTime.now());
            list.add(sysSequence);
        }
        sysSequenceMapper.insertBatchSomeColumn(list);
    }
}

可以看到确实是批量进行插入
sql日志

后记-查询批量新增的额外收获

mp除了提供通过id批量修改,也没有提供根据字段来修改,那么我们可以不可以改造它原有的方法呢?答案是可以的。根据上面的saveOrUpdate源码进行修改。例子如下。

引用别人的方法
但是我不是很满意,觉得入参传一个固定值,这样很傻。所以我找到了一个新办法

接口定义一个新方法
public interface ISysSequenceService extends IService<SysSequence> {
    /**
     * 批量根据指定的updateWrapper进行修改
     * @param entityList
     * @param batchSize
     * @param updateWrapperFunction
     * @param <T>
     * @return
     */
    <T> boolean batchUpdate(Collection<T> entityList, int batchSize, Function<T, LambdaUpdateWrapper> updateWrapperFunction);//这里传的是Function<T, R>, 返回值是一个updateWrapper
}
实现类
@Service
public class SysSequenceServiceImpl extends ServiceImpl<SysSequenceMapper, SysSequence> implements ISysSequenceService {
    @Override
    public <T> boolean  batchUpdate (Collection<T>entityList, int batchSize, Function<T, LambdaUpdateWrapper> updateWrapperFunction) {
        String sqlStatement = getSqlStatement(SqlMethod.UPDATE);//这里修改为UPDATE
        return executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            HashMap<Object, Object> param = CollectionUtils.newHashMapWithExpectedSize(2);
            param.put(Constants.ENTITY, entity);
            param.put(Constants.WRAPPER, updateWrapperFunction.apply(entity));//这里巧妙的用到了entity,做到了灵活赋值,又根据的是你传入的Function来进行操作,非常nice
            sqlSession.update(sqlStatement, param);
        });
    }
}
实际调用(两种写法看你喜欢)
sysSequenceService.batchUpdate(updateList, 100, sysSequence -> Wrappers.<SysSequence>lambdaUpdate().eq(SysSequence::getSeqName, sysSequence.getSeqName()));

sysSequenceService.batchUpdate(updateList, 100, sysSequence -> new LambdaUpdateWrapper<SysSequence>().eq(SysSequence::getSeqName, sysSequence.getSeqName()));

参考

1.自定义sql批量实现原理是继承AbstractMethod重写injectMappedStatement方法

https://blog.csdn.net/sadnskajf/article/details/126748605?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EAD_ESQUERY%7Eyljh-3-126748605-blog-109102424.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EAD_ESQUERY%7Eyljh-3-126748605-blog-109102424.pc_relevant_aa&utm_relevant_index=5

2.实现真正的批量方法

https://blog.csdn.net/qq_50652600/article/details/126038809?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-2-126038809-blog-126134485.pc_relevant_3mothn_strategy_and_data_recovery&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-2-126038809-blog-126134485.pc_relevant_3mothn_strategy_and_data_recovery&utm_relevant_index=5

3.优化方法入参

https://www.panziye.com/java/4641.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值