准备maven 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.2.0</version>
</dependency>
yml 配置
spring:
servlet:
multipart:
max-request-size: 30MB
max-file-size: 1024MB
datasource:
username: root
password: a
url: jdbc:mysql://127.0.0.1:3306/employees?rewriteBatchedStatements=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghai
main:
allow-circular-references: true
Controller
@RestController
public class SalariesController {
@Resource
private ImportService importService;
/**
* java异步导入实战
*
* @param file
* @throws IOException
*/
@PostMapping("import")
public void importExcel(MultipartFile file) throws IOException {
//importService.importExcel(file);
importService.importExcelAsync(file);
}
}
Service
@Service
public class ImportService {
@Resource
private SalariesListener salariesListener;
private ExecutorService executorService = Executors.newFixedThreadPool(20);
public void importExcelAsync(MultipartFile file) {
// 开20个线程分别处理20个sheet
List<Callable<Object>> tasks = new ArrayList<>();
for (int i = 0; i < 20; i++) {
int num = i;
tasks.add(() -> {
EasyExcelFactory.read(file.getInputStream(), Salaries.class, salariesListener)
.sheet(num).doRead();
return null;
});
}
try {
executorService.invokeAll(tasks);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
EasyExcel监听器
@Component
public class SalariesListener extends ServiceImpl<SalariesMapper, Salaries> implements ReadListener<Salaries>, IService<Salaries> {
private static final Log logger = LogFactory.getLog(SalariesListener.class);
private ExecutorService executorService = Executors.newFixedThreadPool(20);
private ThreadLocal<ArrayList<Salaries>> salariesList = ThreadLocal.withInitial(ArrayList::new);
private static AtomicInteger count = new AtomicInteger(1);
private static final int batchSize = 10000;
@Resource
private SalariesListener salariesListener;
@Override
@Transactional(rollbackFor = Exception.class)
public void invoke(Salaries data, AnalysisContext context) {
salariesList.get().add(data);
if (salariesList.get().size() >= batchSize) {
// saveData();
asyncSaveData();
}
}
public void saveOne(Salaries data) {
save(data);
logger.info("第" + count.getAndAdd(1) + "次插入1条数据");
}
public void saveData() {
if (!salariesList.get().isEmpty()) {
saveBatch(salariesList.get(), salariesList.get().size());
logger.info("第" + count.getAndAdd(1) + "次插入" + salariesList.get().size() + "条数据");
salariesList.get().clear();
}
}
public void asyncSaveData() {
if (!salariesList.get().isEmpty()) {
ArrayList<Salaries> salaries = (ArrayList<Salaries>) salariesList.get().clone();
executorService.execute(new SaveTask(salaries, salariesListener));
salariesList.get().clear();
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void doAfterAllAnalysed(AnalysisContext context) {
logger.info("一个Sheet全部处理完");
if (salariesList.get().size() >= batchSize) {
saveData();
}
}
static class SaveTask implements Runnable {
private List<Salaries> salariesList;
private SalariesListener salariesListener;
public SaveTask(List<Salaries> salariesList, SalariesListener salariesListener) {
this.salariesList = salariesList;
this.salariesListener = salariesListener;
}
@Override
public void run() {
salariesListener.saveBatch(salariesList);
logger.info("第" + count.getAndAdd(1) + "次插入" + salariesList.size() + "条数据");
}
}
}
运行结果