标签:
对于上一篇excel中出现的问题,在excel导入导出中都做了优化.还是eclipse+jdk1.8,但是这个项目是一个web项目,需要配合Tomcat服务器,并且使用了SSH框架,
I/O操作过多
首先,对于I/O操作过多,那么就不像之前一样,一条一条的添加或者更新;而且凑齐一堆,也就是一个list集合,然后统一的批量保存.
使用SessionFactory获取当前的session,然后调用session的persist方法,保存实体.只是设置了一个批量的量值.每到30条数据,就将缓存同步到数据库中.
/** * 批量保存 */ @Override public <T> boolean saveEntitys(List<T> entitys) { boolean flag = false; int batchCount = 30; try { for (int i = 1; i <= entitys.size(); i++) { getSession().persist(entitys.get(i - 1)); if (i % batchCount == 0) { getSession().flush(); getSession().close(); } } } catch (Exception e) { e.printStackTrace(); } return flag; }
Excel中英文
然后是excel模板必须固定,不能更换列的顺序,且是英文字段的问题.excel模板必须固定是因为我们没有从excel中获取表头,并记录每个表头是在那一列,这样就可以根据表头中的值,知道列号,然后根据列号去相应的列中取值.
// 将列名和列号放入Map中,这样下面通过列名就能获取列号, LinkedHashMap<String, Integer> colNameNoMap = new LinkedHashMap<String, Integer>(); for (int i = 0; i < excelFiledNameArray.length; i++) { colNameNoMap.put(excelFiledNameArray[i], headerRow[i].getColumn()); }
而英文问题,需要有一个中文对照表,这里就将中文和英文的对照表放到xml文件中.在WebContent文件夹下,放着studentImport.xml的配置文件.内容就是如下,这里面设置了需要导入的excel的实体,导入的excel中的Sheet名称,以及导入的普通字段是中英文对照哪些,主外键关联的字段是哪些(目前还没弄出来),以及唯一不可重复的字段是哪些.而这些配置了的字段,不论是普通字段,主外键关联字段还是唯一键字段都是excel中必须存在的字段的
<?xml version="1.0" encoding="UTF-8"?> <!--导入"Student"(下载Excel模板 和 导入Student Excel) --> <importExcel> <Entity entityName="com.tgb.entity.Student" sheetName="考生"> <!--1:NormalFieldMaps——要导入的实体的 普通字段。。。Start。。。 --> <NormalFieldMaps> <NormalField name="学号" code="stuNo"></NormalField> <NormalField name="姓名" code="stuName"></NormalField> <NormalField name="性别" code="stuSex"></NormalField> <NormalField name="学院" code="department"></NormalField> <NormalField name="专业" code="major"></NormalField> </NormalFieldMaps> <!--1:NormalFieldMaps——要导入的实体的 普通字段。。。End。。。 --> <!--2:UniqueFieldMaps——要导入的实体中,数据不可重复的字段。。。Start。。。 --> <UniqueFieldMaps> <UniqueField name="学号"> </UniqueField> </UniqueFieldMaps> <!--2:UniqueFieldMaps——要导入的"教师"实体中,数据不可重复的字段。。。End。。。 --> </Entity> </importExcel>
而我们需要做的就是用Jdom或者Dom4j工具,读取这个xml文件,获取根节点importExcel,以及他的子节点Entity,可以是多个.并且Entity中的属性,是什么也要获取到.用entityName可以反射出实体的实例,而子节点NormalFieldMaps和UniqueFieldMaps可以将里面的中英文字段对照表放到map中,以及放到list中.只需要解析NormalField节点的属性name和code,以及UniqueField的name属性.
// 记录普通节点 private LinkedHashMap<String, String> normalFieldMap; // 记录唯一节点 private List<String> uniqueFieldList; // 数组的形式记录唯一节点 private String[] uniqueFieldArray = null; /******************** 解析{导入xml}到map中 start *******************************/ /** * 将导入的excel解析 * * @param inXMLFile */ private void importExcelXml2Map(InputStream inXMLFile) { try { // 1.检测 if (null == inXMLFile) { throw new FileNotFoundException(); } // 2.解析,使用dom4j SAXReader reader = new SAXReader(); Document doc = reader.read(inXMLFile); // 根元素,importExcel Element root = doc.getRootElement(); // 3.获取Entity节点 Iterator iterator = root.elements("Entity").iterator(); while (iterator.hasNext()) { Element entityElement = (Element) iterator.next(); // 3.1.获取属性,entityName,以及导入的实体 String entityName = entityElement.attributeValue("entityName"); entityClazz = Class.forName(entityName); // 实例化,普通字段的map和唯一字段的list,用于放xml中的值 // 链表的Map,空间不连续,添加,修改,删除快,读不快 normalFieldMap = new LinkedHashMap<String, String>(); uniqueFieldList = new ArrayList<String>(); // 解析普通字段节点 parseEntityNormalFieldMaps(entityElement); // 解析唯一字段节点 parseEntityUniqueFieldMaps(entityElement); } } catch (Exception e) { e.printStackTrace(); } } /** * 解析普通字段的Map集合节点 * * @param entityElement */ private void parseEntityNormalFieldMaps(Element entityElement) { if (null != entityElement) { Iterator iterator = entityElement.elements("NormalFieldMaps") .iterator(); while (iterator.hasNext()) { Element normalFiledMapsElement = (Element) iterator.next(); parseNormalFieldMaps(normalFiledMapsElement); } } } /** * 解析普通字段,在Map集合下的 * * @param normalFiledMapsElement */ private void parseNormalFieldMaps(Element normalFiledMapsElement) { if (null != normalFiledMapsElement) { Iterator iterator = normalFiledMapsElement.elements("NormalField") .iterator(); while (iterator.hasNext()) { Element normalFileElement = (Element) iterator.next(); // 将节点上的值,赋值到Map中 setNormalFieldMap(normalFileElement); } } } /** * 将普通字段的name和code,赋值到normalFieldMap上 * * @param normalFileElement */ private void setNormalFieldMap(Element normalFileElement) { if (null != normalFileElement) { // 字段中文 String name = normalFileElement.attributeValue("name"); // 字段英文 String code = normalFileElement.attributeValue("code"); normalFieldMap.put(name, code); } } /** * 解析唯一字段的Map集合节点 * * @param entityElement */ private void parseEntityUniqueFieldMaps(Element entityElement) { if (null != entityElement) { Iterator iterator = (Iterator) entityElement.elements( "UniqueFieldMaps").iterator(); while (iterator.hasNext()) { Element uniqueFieldMapsElement = (Element) iterator.next(); parseUniqueFieldMaps(uniqueFieldMapsElement); } } } /** * 解析唯一字段,在map集合下的 * * @param uniqueFieldMapsElement */ private void parseUniqueFieldMaps(Element uniqueFieldMapsElement) { if (null != uniqueFieldMapsElement) { Iterator iterator = (Iterator) uniqueFieldMapsElement.elements( "UniqueField").iterator(); while (iterator.hasNext()) { Element uniqueFieldElement = (Element) iterator.next(); setUniqueFieldList(uniqueFieldElement); } setUniqueFieldArray(uniqueFieldList); } } /** * 将唯一字段的值设置到list中 * * @param uniqueFieldElement */ private void setUniqueFieldList(Element uniqueFieldElement) { if (null != uniqueFieldElement) { // 唯一字段的中文名,对应普通字段的中文名 String name = uniqueFieldElement.attributeValue("name"); uniqueFieldList.add(name); } } /** * 将list中的唯一值,放到Array数组中 * * @param uniqueFieldList */ private void setUniqueFieldArray(List<String> uniqueFieldList) { if (null != uniqueFieldList) { int size = uniqueFieldList.size(); uniqueFieldArray = new String[size]; for (int i = 0; i < size; i++) { uniqueFieldArray[i] = uniqueFieldList.get(i); } } } /******************** 解析{导入xml}到map中 end *********************************/
这样就可以提供这样的excel模板了.表头变为中文,不用写id,hibernate可以用主键生成策略生成id.并且各列的顺序也可以随意,只是必须有这些字段.
Excel路径
而excel的路径写死的问题,只需要一个界面,上传工具.下载的时候通过response放到界面上.
文件上传
jsp页面中的内容,提供上传功能.
<body> <form id="importExcelForm" enctype="multipart/form-data" method="post" > <input id="fileBox" name="fileBox" class="easyui-filebox" style="width:400px" data-options="prompt:'请选择文件'"/> <a id="importButton" href="#" class="easyui-linkbutton" onclick="importExcel()">导入Excel</a> </form> <script type="text/javascript"> //点击弹出选择文件框的按钮 $('#fileBox').filebox({ buttonText :'选择文件', buttonAlign :'right' }); function importExcel() { //获取文件 var fileName = $('#fileBox').filebox('getValue'); //基本的校验 if(fileName == "") { alert("请选择上传的文件!"); }else { var fileNameSuf = /\.[^\.]+$/.exec(fileName); if(fileNameSuf != ".xls") { alert("请选择xls格式文件!"); $("#fileBox").filebox('setValue',''); }else { $('#importExcelForm').form('submit',{ url:"${pageContext.request.contextPath}/importExcel", onSubmit:function() { return $(this).form('validate'); }, success:function(result) { alert(result) var result = eval(result); if(result == false) { $.messager.alert("提示","导入失败,请检查excel数据!","info"); }else if(result == true) { $.messager.alert("提示","导入成功!","info"); }else { $.messager.alert("提示","浏览器不兼容,请更换浏览器","info"); } } }); } } } </script>
而Contorller接收到的上传请求,会将excel和导入xml都变为输入流,然后调用工具类的导入方法,将excel导入.导入方法,主要也是解析导入xml,获取里面的配置,以及获取excel中的数据,将数据变为实体集合,最后将实体集合使用批量保存,保存到数据库中.当然,将excel的数据变成list的时候,需要对数据进行检测,若配置了唯一字段,则该字段中的值,必须是唯一的.
/** * 导入Excel */ @RequestMapping(value="importExcel") private void importExcel( @RequestParam("fileBox") CommonsMultipartFile excelFile, HttpServletRequest request,HttpServletResponse response) { //接收excel文件变为输入流 InputStream inExcelFile = null; try { inExcelFile = excelFile.getInputStream(); } catch (IOException e) { System.err.println("excel读取出错。"); e.printStackTrace(); } //获取xml配置文件,也变为输入流 String xmlFilePath = request.getSession().getServletContext().getRealPath("/")+"studentImport.xml"; File importXMLFile = new File(xmlFilePath); InputStream inXMLFile = null; try { inXMLFile = new FileInputStream(importXMLFile); } catch (FileNotFoundException e) { System.err.println("未找到xml文件。"); e.printStackTrace(); } try { //导入excel到数据库中,使用工具类parseImportExcelXml Boolean flag = parseImportExcelXml.importExcel(inExcelFile,inXMLFile,response); jacksonJsonUntil.beanToJson(response, flag); }catch(Exception e) { System.err.println("excel导入失败。"); e.printStackTrace(); } }
文件下载
下载到界面上,这里创建Workbook不是写一个文件路径,而是写response的输出流.然后添加表头,添加内容,设置excel名,以及response的一些参数,内容类型和头文件.
errorLines = errorMap.size(); // 创建错误列表工作簿和sheet WritableWorkbook ewwb = Workbook.createWorkbook(response .getOutputStream()); WritableSheet eSheet = ewwb.createSheet("错误列表", 0); // 添加表头 for (int n = 0; n < excelFiledNameArray.length; n++) { Label label = new Label(n, 0, excelFiledNameArray[n]); eSheet.addCell(label); } // 并添加两列,错误原因和所在行号 eSheet.addCell(new Label(excelFiledNameArray.length, 0, "错误原因")); eSheet.addCell(new Label(excelFiledNameArray.length + 1, 0, "所在行号")); // 添加内容 ... addErrorRow(sheet, eSheet, errorMap); // 设置文件名,当前时间 String fileName = new SimpleDateFormat("yyyyMMddhhmmss") .format(new Date()).toString() + "ErrorData"; response.reset(); response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xls"); //设置错误excel的单元格大小 setColumnAutoSize(eSheet, 10); //写入数据,并关闭工作簿 ewwb.write(); ewwb.close();
附注
运行的效果
点击导入Excel,导入成功,数据库中的数据如下.
再次导入Excel,由于数据重复,所以,生成错误excel,并且数据不写入数据库中.
项目下载:Excel导入导出优化
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/liuyanlinglanq/article/details/47833151