各种报错不好处理: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); } }按上面的步骤来,百万级数据导入数据库,内存不会崩,速度也能提上来,出了问题也有办法查。你们可以试试,挺好用的。