背景:
根据项目需求,一次性将10万条Excel数据插入数据库中,如果使用传统的poi解析Excel然后执行10万次insert语句,整个导入过程将非常耗时,poi解析数据非常耗内存,在实际生产环境中使用也会报错等等。
所以分两步优化,解析Excel使用阿里巴巴项目组提供了easyexcel工具类(注意:读取文件务必使用2.0.5+版本,其他版本可能会有bug),将Excel解析成list
1、controller
@RequestMapping("importData")
public ReturnData importData(@RequestParam("file") MultipartFile file) {
logger.info("上传excel文件名称:",file.getOriginalFilename());
ReturnData returnData;
try {
returnData = importDataService.importData(file);
} catch (Exception e) {
e.printStackTrace();
returnData = new ReturnData(0, null, "Excel导入数据异常!");
}
return returnData;
}
2、service
/**
* 上传Excel文件
* @param file
* @return
*/
@Override
public ReturnData importData(MultipartFile file) {
if (file == null) {
return new ReturnData(0, null, "上传文件为空!");
}
List importDataVoList;
InputStream inputStream = null;
try {
inputStream = file.getInputStream();
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
if (!"xls".equals(suffix) && !"xlsx".equals(suffix)) {
return new ReturnData(0, null, "请上传Excel格式的文件");
}
importDataVoList = ExcelUtil.readExcelWithModel(inputStream, ImportDataVo.class,suffix);
if(importDataVoList.size() <= 0){
return new ReturnData(0,null,"Excel中没有可上传的数据");
}
List importDatalist = new ArrayList<>();
for (Object importDataVo : importDataVoList) {
ImportData importData = new ImportData();
BeanUtils.copyProperties(importDataVo , importData );
importDatalist.add(importData);
}
//批量插入数据
int rows = 0;
try {
rows = batchInsertData(importDatalist); //切割list插入数据库
logger.info("导入成功,共插入:" + rows + "条数据!");
}catch (Exception e){
e.printStackTrace();
}
return new ReturnData(1,rows,"success");
} catch (Exception e){
e.printStackTrace();
logger.info("导入数据异常!");
return new ReturnData(1,null,"导入数据异常");
}
}
//分隔为每500条批量入库
private int batchInsertData(List importDatalist) {
int rows = 0;
for (int i = 0; i < importDatalist.size()/500 + 1 ; i++) {
int end = (i+1)* 500;
if(end >= importDatalist.size()){
end = importDatalist.size();
}
List importDatas = importDatalist.subList(i*500,end);
int flag = importDataMapper.batchInsertOrUpdateList(importDatas);
if(flag > 0 ){
rows = rows + end-(i*500);
}
}
return rows;
}
3、mybatis的xml文件
注意:insert语句后的ON DUPLICATE KEY UPDATE 子句,表示如果插入的行与表中现有数据的唯一索引或者主键重复的话,则会更新该条数据;如果插入的行与表中现有数据的唯一索引或者主键不重复,则执行新记录插入操作。
SELECT LAST_INSERT_ID()
insert into import_data
(user_num, user_name, IDcard,
age, gender,address,native_place,phone)
values
(
#{importData.userNum},
#{importData.userName},
#{importData.idcard},
#{importData.age},
#{importData.gender},
#{importData.address},
#{importData.nativePlace},
#{importData.phone}
)
ON DUPLICATE KEY UPDATE
user_num= VALUES(user_num),
user_name= VALUES(user_name),
IDcard= VALUES(IDcard),
age= VALUES(age),
gender= VALUES(gender),
address= VALUES(address),
native_place= VALUES(native_place),
phone= VALUES(phone)