不同方式通过POI读取xlsx文件后由Mybatis向MySQL写入30万条数据性能分析

情景:有一个一条记录有20个字段,共30万条记录的xlsx文件,文件大小大约30兆,需要将其内容读取后写入数据库。

这意味着需要向数据库写入30万条数据,读取xlsx方面,用的是POI,用了几种方式做,性能对比如下:

一、MyBatis的xml里逐条写入:一条记录生成一条SQL语句并执行一次,这种做法性能过低,不作讨论。

二、MyBatis的xml里用foreach方式,一次写入2000条数据

1.POI:XSSFWorkbook读xlsx:

List<List<Object>> result = new ArrayList<>();
		Workbook wb = new XSSFWorkbook(new File(filePath));
		for (Sheet sheet : wb) {
			for (Row row : sheet) {
				List<Object> rowData = getRowData(row);
				result.add(rowData);
			}
		}
		wb.close();
		return result;

   这种方式在xlsx内有30万条数据的情况下报错。

    2.POI:SAX模式读xlsx:

File xlsxFile = new File(filePath);
		OPCPackage p = OPCPackage.open(xlsxFile.getPath(), PackageAccess.READ);
		Xlsx2ListData xlsx2ListData = new Xlsx2ListData(p, minColumns, null);
		List<List<Object>> result = xlsx2ListData.process();
		p.close();
		return result;

使用该方法一次读取30万条数据,形成一个size=30万的list,然后每次写入2000条数据,耗时1320s。

3.POI:SAX方式读取xlsx文件,读取每一行调用RowDataProcesser:

File xlsxFile = new File(filePath);
		OPCPackage p = OPCPackage.open(xlsxFile.getPath(), PackageAccess.READ);
		Xlsx2ListData xlsx2ListData = new Xlsx2ListData(p, minColumns,
				rowDataProcesser);
		xlsx2ListData.process();
		p.close();

该方式使用RowDataProcesser接口,读取一行就写入list,list满后向数据库批量写入,同样的机器,同样的数据,耗时1356s

第二,第三种方式读取xlsx文件分别耗时:13s,12s也就是说,数据库IO占了大部分时间。

结论:SAX模式读取大文件(30万条数据)向数据库写入的情景下,SAX模式的两种读取方式性能差距不大,真正的性能瓶颈是数据库I/O,针对这个问题,尝试增大list容量,减少数据库IO次数。

将list容量增大至1万,即每次向数据库写入1万条数据。

结果报错了:PacketTooBigException

目前发现最快的方法:

直接上代码:

SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
    SimpleTableMapper mapper = session.getMapper(SimpleTableMapper.class);
    List<SimpleTableRecord> records = getRecordsToInsert(); // not shown

    BatchInsert<SimpleTableRecord> batchInsert = insert(records)
            .into(simpleTable)
            .map(id).toProperty("id")
            .map(firstName).toProperty("firstName")
            .map(lastName).toProperty("lastName")
            .map(birthDate).toProperty("birthDate")
            .map(employed).toProperty("employed")
            .map(occupation).toProperty("occupation")
            .build()
            .render(RenderingStrategy.MYBATIS3);

    batchInsert.insertStatements().stream().forEach(mapper::insert);

    session.commit();
} finally {
    session.close();
}

基本思想是将 MyBatis session 的 executor type 设为 Batch ,然后多次执行插入语句。

好的,下面是一个使用Spring Boot和POI读取xlsx文件,并将数据存入MySQL数据库的示例代码,附有详细注释: 首先,需要在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency> ``` 接着,创建一个实体类,用于存储读取数据。示例代码如下: ```java public class Employee { private String name; private int age; private String gender; private String department; // 省略getter和setter方法 } ``` 然后,创建一个Service类,用于读取xlsx文件并将数据存入MySQL数据库。示例代码如下: ```java @Service public class EmployeeService { @Autowired private JdbcTemplate jdbcTemplate; public void readExcelFile(String filePath) throws IOException { // 创建一个输入流,读取Excel文件 FileInputStream inputStream = new FileInputStream(filePath); // 创建一个Workbook对象,表示整个Excel文件 Workbook workbook = new XSSFWorkbook(inputStream); // 获取第一个Sheet页 Sheet sheet = workbook.getSheetAt(0); // 遍历Sheet页中的每一行数据 for (Row row : sheet) { // 跳过第一行表头 if (row.getRowNum() == 0) { continue; } // 创建一个Employee对象,用于存储当前行的数据 Employee employee = new Employee(); // 遍历当前行中的每一个单元格数据 for (Cell cell : row) { switch (cell.getColumnIndex()) { case 0: employee.setName(cell.getStringCellValue()); break; case 1: employee.setAge((int) cell.getNumericCellValue()); break; case 2: employee.setGender(cell.getStringCellValue()); break; case 3: employee.setDepartment(cell.getStringCellValue()); break; default: break; } } // 将Employee对象中的数据插入到MySQL数据库中 jdbcTemplate.update("INSERT INTO employee(name, age, gender, department) VALUES (?, ?, ?, ?)", employee.getName(), employee.getAge(), employee.getGender(), employee.getDepartment()); } // 关闭输入流和Workbook对象 inputStream.close(); workbook.close(); } } ``` 最后,创建一个Controller类,用于接收上传的xlsx文件并调用Service类的方法读取并存储数据。示例代码如下: ```java @RestController public class EmployeeController { @Autowired private EmployeeService employeeService; @PostMapping("/upload") public void uploadFile(@RequestParam("file") MultipartFile file) throws IOException { // 保存上传的文件到本地 String filePath = "D:/upload/" + file.getOriginalFilename(); file.transferTo(new File(filePath)); // 调用Service类的方法读取并存储数据 employeeService.readExcelFile(filePath); } } ``` 以上就是一个使用Spring Boot和POI读取xlsx文件,并将数据存入MySQL数据库的示例代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值