insertBatchSomeColumn是一种Mybatis Plus实现的批量插入小帮手

2024-01-03 12:44:43

一、insertBatchSomeColumn

1.首先在config目录下新建一个sql注入器

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;

import java.util.List;
 
public class InsertBatchSqlInjector extends DefaultSqlInjector {

@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        // super.getMethodList() 保留 Mybatis Plus 自带的方法
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
        // 添加自定义方法:批量插入,方法名为 insertBatchSomeColumn
        methodList.add(new InsertBatchSomeColumn());
        return methodList;
        }
}

2.讲这个sql注入器交给Spring管理


import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
 
@Component
public class MybatisPlusConfig {
   
        @Bean
        public InsertBatchSqlInjector insertBatchSqlInjector() {
        return new InsertBatchSqlInjector();
        }

}

3 自定义一个MyBaseMapper继承MyBatis Plus 中的BaseMapper中所有的方法,

这样的话就可以在MyMapper中获得一个新的方法就是insertBatchSomeColumn 则可以直接调用此方法。

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.UserStudy;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
 
import java.util.List;
 
public interface MyBaseMapper extends BaseMapper<T> {
    void insertBatchSomeColumn(@Param("list") List<T> batchList);
}

4创建实体类的Mapper使其继承一个MyBaseMapper



public interface UserMapper extends MyBaseMapper<User>{


}

5 测试代码,调用insertBatchSomeColumn方法

    @Resource
    private UserMapper userMapper;
 
    /*
     *批量插入
     */
    @Override
    public void insertDataOfUser() {
        List<User> userList = Lists.newArrayList;
        UserS user1 = new User();
        user1.setName("张三");
        User userStudy2 = new User();
        user2.setName("李四");
        userList.add(user1);
        userList.add(user2);
        //调用insertBatchSomeColumn方法
        userMapper.insertBatchSomeColumn(userList);
        //调用IService的saveBatch方法
        //this.saveBatch(userList,2000);
    }

注意:SQL有语句长度限制,在MySQL中被参数max_allowed_packet限制,默认为1M,如果拼接长度超过此限制就会报错,两种解决方式,一个是调整MySQL的max_allowed_packet 限制,另一个则是通过代码控制每次的提交数量。

PS:

//**

下面的方式仅仅是为了适用多线程对于数据插入增加效率的

**//

5调用此方法时还可以使用一个线程池进行多线程操作。

第一步创建一个线程池.

@EnableAsync是Spring框架中的一个注解,用于开启对异步方法的支持。当我们在Spring配置类上使用该注解时,它会告诉Spring容器启用异步方法处理功能。

在开启了@EnableAsync的环境下,所有标记了@Async注解的方法都会被异步执行,即这些方法不会阻塞当前线程,而是交给后台任务执行器(如ThreadPoolTaskExecutor)在单独的线程中运行。

@Configuration
@EnableAsync
@Slf4j
public class ThreadPoolExecutorConfig {

    /**
     *   corePoolSize: 8 # 核心线程数
     *     maxPoolSize: 20 # 设置最大线程数
     *     keepAliveSeconds: 300 # 设置线程活跃时间
     *     queueCapacity: 100 # 设置队列容量
     *     prefixName: async-service- # 线程名称前缀
     * @return
     */
    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        log.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(8);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(100);
        executor.setKeepAliveSeconds(300);
        executor.setThreadNamePrefix("async-service-");
        // 拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }


}

6创建一个接口 对于使用多线程插入的操作进行封装和实现

public interface IAsyncInsertService {
   void executeAsyncInsert(List<T> list, MyBaseMapper mapper, CountDownLatch countDownLatch);

}

7实现接口方法

对于?@Async("asyncServiceExecutor") @Async是一个用于声明异步方法的注解,主要应用于Spring框架。当一个方法被@Async注解修饰时,意味着该方法将不在调用线程中同步执行,而是由Spring框架托管到一个后台任务执行器(如ThreadPoolTaskExecutor)中以异步的方式运行。

@Service
@Slf4j
public class AsyncInsertServiceImpl implements IAsyncInsertService {
    @Override
    @Async("asyncServiceExecutor")
    public void executeAsyncInsert(List<T> list, MyBaseMapper mapper, CountDownLatch countDownLatch) {
        try {
            log.info("开始异步线程......");
            // 异步线程需要做的事情
           mapper.insertBatchSomeColumn(list);
            log.info("结束异步线程!");
        } finally {
            // 无论上面程序是否异常必须执行 countDown,否则 await 无法释放
            countDownLatch.countDown();
        }

8在实际任务中调用异步插入方法

对于异步插入实现方法,只用调用insert方法即可实现异步插入,提升运行效率

  /**
     * 批量插入数据
     * @param dataList
     * @param mapper
     */
    public void insert(List dataList, MyBaseMapper mapper){
        // 每500 条数据插入开一个线程
        List<List<T>> lists = Lists.partition(dataList, batchSize);
        CountDownLatch countDownLatch = new CountDownLatch(lists.size());
        long startTime = System.currentTimeMillis();
        lists.forEach(listSub -> asyncInsertService.executeAsyncInsert(listSub, mapper, countDownLatch));
        try {
            // 保证之前的所有的线程都执行完成,才会走下面的
            countDownLatch.await();
        } catch (InterruptedException e) {
            log.error("阻塞异常:" + e.getMessage());
        }
        long endTime = System.currentTimeMillis();
        log.info("批量插入数据共耗时:{} 毫秒", endTime - startTime );
    }

总结:默认的insert的方法对寻常业务来说是非常之高效,但对于批量数据的产生确实灾难性的,就是慢,很慢,巨慢,IService的saveBatch方法优于默认的insert方法,但是我选通过SQL注入器的方法insertBatchSomeColumn。

文章来源:https://blog.csdn.net/qq_53170175/article/details/135334910
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。