需求:从Excel导入大量客户数据。java
项目环境:Spring+SpringMVC+MyBatis。数组
技术:使用jxl实现Excel数据导入。bash
方案:上传Excel文件,Controller层读取每行的内容保存到对象中,存储到List中。将保存数据的List每100个对象切割并存放到一个新的List 中的,而后使用一个大的List保存这些切割好的List组,传递到Service层遍历大List,将每组List传入的Dao层,让MyBatis批量插入数据。app
废话很少说,上代码ide
//获取用户上传的文件
Workbook workbook = Workbook.getWorkbook(file.getInputStream());
//获取工做簿sheet
Sheet sheet = workbook.getSheet(0);
//获取总行数
int rows = sheet.getRows();
//获取数据表中的数据
List<Product> productList = new ArrayList<>();
Cell cell;
for (int i = 1; i < rows; i++) {
//该对象请放在循环内部,让GC进行回收,放在循环外面。List集合中的全部对象引用相同,保存的全部对象都是同一个引用
Product product = new Product();
//判断名称是否为空,若是为空结束遍历
if (StringUtil.isEmpty(sheet.getCell(0, i).getContents())) {
break;
}
product.setId(sheet.getCell(0, i).getContents());
product.setContent(sheet.getCell(1, i).getContents());
//读取时间类型
cell = sheet.getCell(2, i);
if (cell.getType() == CellType.DATE) {
DateCell dc = (DateCell) cell;
Date date = dc.getDate();
Calendar c = Calendar.getInstance();
c.setTime(date);
//解析excel的时候会默认当前输入的时间为格林威治时间,须要转为当前时区的时间(以前8小时)
c.add(Calendar.HOUR, -8);
product.setSr_material_date(c.getTime());
}
//读取时间类型
cell = sheet.getCell(3, i);
if (cell.getType() == CellType.DATE) {
DateCell dc = (DateCell) cell;
Date date = dc.getDate();
Calendar c = Calendar.getInstance();
c.setTime(date);
//解析excel的时候会默认当前输入的时间为格林威治时间,须要转为当前时区的时间(以前8小时)
c.add(Calendar.HOUR, -8);
product.setDate(c.getTime());
}
product.setAddress(sheet.getCell(4, i).getContents());
product.setTel(sheet.getCell(5, i).getContents());
//将当前对象保存的productList中
productList.add(product);
}
//关闭资源
workbook.close();
//List切割
List<List<Product>> dataList = new ArrayList<>();
int listSize = productList.size();
int toIndex = 100;
int keyToken = 0;
for (int i = 0; i < productList.size(); i += 100) {
// 做用为toIndex最后没有100条数据则剩余几条newList中就装几条
if (i + 100 > listSize) {
toIndex = listSize - i;
}
List<Product> newList = productList.subList(i, i + toIndex);
dataList.add(newList);
}
//将切割并打包好的List集合传递到Service层进行处理
productService.importFromExcel(dataList);
复制代码
@Override
@Transactional
public int importFromExcel(List<List<Product>> dataList) throws Exception {
int result = 0;
try {
if (dataList != null) {
for (List<SafeRepository> list : dataList) {
result += productMapper.batchInsertProduct(list);
}
}
} catch (Exception e) {
logger.error(e.getMessage());
throw e;
}
return result;
}
复制代码
<insert id="batchInsertProduct" parameterType="java.util.List">
INSERT INTO t_product
(
id,
content,
date,
address,
tel
)
VALUES
<foreach collection="list" item="pd" index="index" separator=",">
(#{pd.id},
#{pd.content},
#{pd.date},
#{pd.address},
#{pd.tel})
</foreach>
</insert>
复制代码
刚开始想使用Lambda的partition进行List切割的,可是发现切割后返回的是List的父类Collection,看了下ArrayList源码,在将Collection转换为List操做是使用的遍历转换,先转换为对象数组。若是我这里要这么实现须要将切割后获得的Collection遍历转换为对象数组,再将对象数组中的多个元素中的多个对象转换出来,就会出现n*x的时间复杂度,并非我须要的方式。就暂时使用上述的方法进行List切割。this
ArrayList源码spa
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's * iterator. * * @param c the collection whose elements are to be placed into this list * @throws NullPointerException if the specified collection is null */ public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } } 复制代码
我写的Lambda切割List代码excel
List<List<Product>> dataList = new ArrayList<>();
Collection<List<Product>> partition = partition(productList, 100);
复制代码
技术通常般,文章中有什么说的不对的地方,或者有更好的解决办法,还望多指教。谢谢。code