闽公网安备 35020302035485号
各种报错不好处理:Excel里数据格式不对、前后不一致,数据库连接突然断了,或者数据重复,这些情况都可能让导入中断,还容易导致数据乱掉。
数据库批量插入更高效:别一条一条插,批量插入能减少和数据库的交互次数,数据库压力小了,速度也快了。不同数据库批量插入语法不一样,MySQL可以用INSERT INTO ... VALUES (...) , (...),Oracle能用INSERT ALL;搭配MyBatis这类框架,批量插入也很好实现。
public class MyDataModelListener extends AnalysisEventListener<MyDataModel> {
// 每攒够1000条数据,就批量处理一次,这个数量可以根据实际情况调整
private static finalint BATCH_SIZE = 1000;
// 用来存批量数据的列表
private List<MyDataModel> batch = new ArrayList<>();
// 操作数据库的服务类,通过它把数据存到数据库
private MyDataService myDataService;
// 初始化监听器时,传入数据库服务,方便后续调用存储方法
public MyDataModelListener(MyDataService myDataService) {
this.myDataService = myDataService;
}
// 每读一行数据,就会触发这个方法
@Override
public void invoke(MyDataModel data, AnalysisContext context) {
// 先校验数据是否有效,比如检查必填字段、格式是否正确等
if (validateData(data)) {
// 有效数据就加到批量列表里
batch.add(data);
} else {
// 无效数据可以记日志,或者单独存起来后续处理,这里先简单跳过
}
// 当批量列表里的数据够1000条了,就调用处理方法
if (batch.size() >= BATCH_SIZE) {
processBatch();
}
}
// 整个Excel读完后,会触发这个方法
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 剩下没处理的少量数据,最后再处理一次,避免遗漏
if (!batch.isEmpty()) {
processBatch();
}
}
// 批量处理数据,这里是调用服务类的批量存储方法
private void processBatch() {
myDataService.saveBatch(batch);
// 存完后清空列表,准备接收下一批数据
batch.clear();
}
// 堆代码 duidaima.com
// 数据校验的方法,根据自己的业务逻辑写校验规则
private boolean validateData(MyDataModel data) {
// 比如检查某个必填字段是否为空,或者格式是否符合要求
// 符合条件返回true,否则返回false
return true; // 这里只是示例,实际要写具体校验逻辑
}
}
如果Excel有多个Sheet,用线程池同时读多个Sheet,效率更高:// Excel文件的路径
String filePath = "/users/workspace/excel/test.xlsx";
// 要读取的Sheet数量
int numberOfSheets = 20;
// 建一个和Sheet数量一样的线程池,每个线程处理一个Sheet
ExecutorService executor = Executors.newFixedThreadPool(numberOfSheets);
// 循环每个Sheet,让线程池里的线程去处理
for (int sheetNo = 0; sheetNo < numberOfSheets; sheetNo++) {
// 因为lambda表达式里不能直接用循环变量,所以定义一个final变量
int finalSheetNo = sheetNo;
// 把读取Sheet的任务提交给线程池
executor.submit(() -> {
// 读指定的Sheet,用上面定义的监听器处理数据
EasyExcel.read(filePath, MyDataModel.class , new MyDataModelListener(myDataService))
.sheet(finalSheetNo) // 指定要读取的Sheet编号
.doRead(); // 开始读取
});
}
// 所有任务提交完,关闭线程池,不再接受新任务
executor.shutdown();
try {
// 等待所有线程都执行完,这里设置最长等待时间为无限长,直到所有任务完成
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
4、数据读出来了 怎么存进数据库<!-- 批量插入的方法,参数是list集合 -->
<insert id = "saveBatch" parameterType = "list">
INSERT INTO your_table (column1, column2, column3)
VALUES
<!-- 循环list里的每个item,用逗号分隔每个值组 -->
<foreach collection = "list" item = "item" separator = ",">
(#{item.column1}, #{item.column2}, #{item.column3})
</foreach>
</insert>
这里的your_table是你的表名,column1, column2, column3是表的字段名,item.column1这些对应实体类的属性名,要和你的实际情况对应上。// 加事务注解,确保批量插入要么全成功,要么全失败,保持数据一致性
@Transactional
public void saveBatch(List<MyDataModel> dataList) {
try {
// 调用mapper的批量插入方法
myMapper.saveBatch(dataList);
} catch (Exception e) {
// 记日志,方便后面排查问题
log.error("数据插入失败", e);
// 抛异常,触发事务回滚,之前插的会被撤销,避免数据乱掉
throw new RuntimeException("数据插入失败", e);
}
}
按上面的步骤来,百万级数据导入数据库,内存不会崩,速度也能提上来,出了问题也有办法查。你们可以试试,挺好用的。