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;
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class MybatisPlusConfig {
@Bean
public InsertBatchSqlInjector insertBatchSqlInjector() {
return new InsertBatchSqlInjector();
}
}
这样的话就可以在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);
}
public interface UserMapper extends MyBaseMapper<User>{
}
@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 限制,另一个则是通过代码控制每次的提交数量。
第一步创建一个线程池.
@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;
}
}
public interface IAsyncInsertService {
void executeAsyncInsert(List<T> list, MyBaseMapper mapper, CountDownLatch countDownLatch);
}
对于?@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();
}
对于异步插入实现方法,只用调用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。