├── .idea ├── $PRODUCT_WORKSPACE_FILE$ ├── .gitignore ├── compiler.xml ├── encodings.xml ├── misc.xml └── vcs.xml ├── README.md ├── pom.xml ├── qrcode.jpg └── src ├── main └── java │ └── com │ └── github │ └── liuhuagui │ └── gridexcel │ ├── GridExcel.java │ ├── bean │ └── CellListOfRow.java │ ├── eventmodel │ ├── EventModel.java │ ├── XLSEventModel.java │ └── XLSXEventModel.java │ ├── usermodel │ ├── read │ │ ├── ReadExcel.java │ │ ├── ReadExcelByEventModel.java │ │ └── ReadExcelByUserModel.java │ └── write │ │ ├── WriteExcel.java │ │ ├── WriteExcelByStreaming.java │ │ └── WriteExcelByUserModel.java │ └── util │ ├── Assert.java │ └── ExcelType.java └── test ├── java ├── ReadTest.java ├── WriteTest.java ├── bean │ ├── Consultant.java │ ├── Customer.java │ ├── Payment.java │ ├── Provider.java │ ├── Service.java │ └── TradeOrder.java └── util │ ├── MockData.java │ └── PaymentWays.java └── resources ├── 2003.xls ├── 2007.xlsx ├── datasource.obj ├── test.xls └── test.xlsx /.idea/$PRODUCT_WORKSPACE_FILE$: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | - **[快速使用](https://github.com/liuhuagui/gridexcel#%E5%BF%AB%E9%80%9F%E4%BD%BF%E7%94%A8)** 2 | - **[支持流式API](https://github.com/liuhuagui/gridexcel#流式api)** 3 | - **[支持无实体类读写Excel](https://github.com/liuhuagui/gridexcel#无实体类读写Excel)** 4 | 5 | --- 6 | ## 介绍 7 | ### GridExcel 8 | > Universal solution for reading and writing simply Excel based on functional programming and POI EventModel 9 | 10 | GridExcel是基于Java8函数式编程和POI EventModel实现的用于Excel简单读写的通用解决方案。 11 | 12 | - 基于POI EventModel,在读写数据量非常大的Excel时,降低内存占用避免OOM与频繁FullGC 13 | - 基于函数编程,支持关联对象等多种复杂情况的处理,学习成本低 14 | - 支持流式API,使代码编写和理解更简单,更直观 15 | - 支持使用`滚动窗口`+`监听函数`的方式去处理从Excel中读取的数据 16 | 17 | ### Apache POI 18 | 在业务开发中我们经常会遇到Excel的导入导出,而 **Apache POI** 是Java开发者常用的API。 19 | 【[https://poi.apache.org/components/spreadsheet/index.html](https://poi.apache.org/components/spreadsheet/index.html)】 20 | 21 | ### EventModel 22 | 什么是**EventModel**?在**POI FAQ**(常见问题解答)【[https://poi.apache.org/help/faq.html#faq-N100C2](https://poi.apache.org/help/faq.html#faq-N100C2)】官方给出解释: 23 | > The SS eventmodel package is an API for reading Excel files without loading the whole spreadsheet into memory. It does require more knowledge on the part of the user, but reduces memory consumption by more than tenfold. It is based on the AWT event model in combination with SAX. If you need read-only access, this is the best way to do it. 24 | 25 | SS eventmodel包是一个用于读取Excel文件而不将整个电子表格加载到内存中的API。 它确实需要用户掌握更多知识,但是将内存消耗减少了十倍以上。 它基于AWT(Abstract Window Toolkit)event model与SAX的结合。 如果您需要只读访问权限,这是最好的方式。 26 | 27 | ### 函数编程 28 | 说到函数编程,就不得不提**Lambda表达式**,如果对Java8中的Lambda不了解或理解不深刻,可以看下甲骨文官网给出的这篇文章,【[https://www.oracle.com/technetwork/articles/java/architect-lambdas-part1-2080972.html](https://www.oracle.com/technetwork/articles/java/architect-lambdas-part1-2080972.html)】,个人认为这是Java8 Lambda从入门到进阶最好的文章之一。 29 | 30 | 其中函数编程的目的就是实现`代码块传递`,即,将方法作为参数在方法间传递。为此,随着Java语言的发展,不断出现一些解决方案: 31 | 1. Java 1.0, 使用Abstract Window Toolkit (AWT) EventModel来实现,但笨拙且不可行 32 | 2. Java 1.1,提出一系列“Listeners” 33 | 3. 后来使用`内部类`和`匿名内部类`来实现,但是大多数情况下,它们只是被用作事件处理。 34 | 4. 再后来发现更多地方将代`码块作为对象`(实际上是数据)不仅有用而且是必要的,但是Java中函数编程还是很笨拙,它需要成长。 35 | 5. 直到Java 1.7,Java引入了java.lang.invoke包,提供一 种新的动态确定目标方法的机制(可以不用再单纯依靠固化在虚拟机中的字节码调用指令),称为MethodHandle,模拟字节码的方法指针调用,类似于C/C++的**Function Pointer**(函数指针)并引入第5条方法调用的字节码指令**invokedynamic**。 36 | 6. 直到Java 1.8,基于Java 1.7提出的字节码指令**invokedynamic**,实现了Lamda技术,将函数作为参数在方法间传递,Java开始更好的支持函数式编程。 37 | 7. 用反射不是早就可以实现了吗?Reflection API 重量级,性能低。 38 | 39 | 注意: **5、6、7**参考《深入理解Java虚拟机》第2版,8.3.3 动态类型语言支持。 40 | 41 | --- 42 | ## 解决两个问题 43 | **在POI的使用过程中,对大多数API User来说经常面临两个问题,这也是**GridExcel**致力解决的问题。** 44 | ### 问题1. 仅使用简单的导入导出功能,但每次业务的数据对象结构不同,需要重新编写处理方法,很麻烦! 45 | #### 解决方法 46 | 将Excel读写逻辑抽取出来,只关注业务逻辑,封装成工具类。 47 | #### 封装条件 48 | 与大多数Java API一样,POI把更多的精力放在高级功能的处理上,比如Formula(公式)、Conditional Formatting(条件格式)、Zoom(缩放)等。对于仅仅做数据导入导出功能的API User,很少使用这些高级特性,这允许API用户对POI的使用进行简单的封装。 49 | #### 封装方式 50 | 无论是读是写,我们都需要解决Excel中的Columns(列)与Java数据对象Fields(字段)的映射关系,将这种映射关系作为参数(Map对象HashMap或LinkedHashMap),传递给工具类。 51 | 52 | 对于Columns不难理解,它可以是有序的数字或字母,也可以是其它字符串用来作为首行,表示该列数据的含义。 53 | 54 | 对于Fields,它的处理需要兼容复杂情况,如下: 55 | 56 | - 查询字段时出现异常 57 | - 字段或单元格的值为null 58 | - 该列的值可能对应关联对象、甚至是关联集合中的某个字段值 59 | - 字段或单元格的值需要做特殊处理,例如`value == true?完成:失败;` 60 | 61 | ##### 反射 62 | 首先想到,也是大多数封装者都在使用的方式是就是**Reflection API**,从上文 **[函数编程](https://blog.csdn.net/qq_32331073/article/details/97650960#_15)** 章节我们了解到,反射重量级,会降低代码的性能,同时对复杂情况的处理支持性不够好。 63 | ##### 反射+注解 64 | 这种方式可以更好的支持复杂情况,但是反射依然会降低性能,同时注解对数据对象会造成代码侵入,而且对该工具类封装者的其他使用者无疑会增加学习成本。 65 | ##### 匿名内部类—— 作为监听函数 66 | 这种方式也可以很好的支持复杂情况,但是使用匿名内部类的语法显然患有“垂直问题”(这意味着代码需要太多的线条来表达基本概念),太过冗杂。~~至于性能,应该也不如直接传递函数来的快吧。~~ 67 | ##### 函数接口(Lambda)—— 作为监听函数 68 | 这种方式是基于第5条方法调用的字节码指令**invokeDynamic**实现的,直接传递函数代码块,很好的支持复杂情况,性能较高,代码编写更简单结构更加简洁,而且对数据对象代码零侵入。 69 | 70 | ### 问题2. Excel一次性导入或导出数据量比较大,造成`内存溢出`或`频繁的Full GC`,该如何解决? 71 | #### 解决方法 72 | - 读Excel —— eventmodel 73 | - 写Excel —— streaming.SXSSFWorkbook 74 | #### 原理 75 | POI的使用对我们来说很常见,对下面两个概念应该并不陌生: 76 | - HSSFWorkbook(处理97(-2007) 的.xls) 77 | - XSSFWorkbook(处理2007 OOXML (.xlsx) ) 78 | 79 | 但是对于**eventmodel**和**streaming.SXSSFWorkbook**就很少接触了,它们是POI提供的专门用来解决内存占用问题的**low level API**(低级API),使用它们可以读写数据量非常大的Excel,同时可以避免`内存溢出`或`频繁的Full GC`。【https://poi.apache.org/components/spreadsheet/how-to.html】 80 | - **eventmodel**,用来读Excel,并没有将Excel整个加载到内存中,而是允许用户从**InputStream**每读取一些信息,就交给**回调函数**或**监听器**,至于丢弃,存储还是怎么处理这些内容,都交由用户。 81 | - **streaming.SXSSFWorkbook**,用来写Excel(是对XSSFWorkbook的封装,仅支持.xlsx),通过**滑动窗口**来实现,只在内存中保留滑动窗口允许存在的行数,超出的行Rows被写出到临时文件,当调用`write(OutputStream stream)`方法写出内容时,再直接从临时内存写出到目标**OutputStream**。**SXSSFWorkbook**的使用会产生一些局限性。 82 | - Only a limited number of rows are accessible at a point in time. 83 | - Sheet.clone() is not supported. 84 | - Formula evaluation is not supported 85 | #### 解决途径 86 | - https://github.com/liuhuagui/gridexcel 87 | 基于Java函数编程(Lambda),支持流式API,使用环境Java1.8或更高,学习成本:Lambda 88 | 89 | 实际上POI官网已经给了用户使用示例,而上述工具只是做了自己的封装实现,让使用更方便。 90 | 91 | --- 92 | ### 快速使用 93 | ```xml 94 | 95 | com.github.liuhuagui 96 | gridexcel 97 | 2.3 98 | 99 | ``` 100 | #### GridExcel.java 101 | GridExcel.java提供了多种静态方法,可以直接使用,具体式例可参考测试代码(提供了测试数据和测试文件): 102 | - https://github.com/liuhuagui/gridexcel/blob/master/src/test/java/ReadTest.java 103 | - https://github.com/liuhuagui/gridexcel/blob/master/src/test/java/WriteTest.java 104 | #### 流式API 105 | ```java 106 | /** 107 | * 业务逻辑处理方式三选一: 108 | * 1.启用windowListener,并将业务逻辑放在该函数中。 109 | * 2.不启用windowListener,使用get()方法取回全部数据集合,做后续处理。 110 | * 3.readFunction函数,直接放在函数中处理 或 使用final or effective final的局部变量存放这写数据,做后续处理。 111 | * 注意:使用EventModel时readFunction函数的输入为每行的cell值集合List。 112 | * @throws Exception 113 | */ 114 | @Test 115 | public void readXlsxByEventModel() throws Exception { 116 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("2007.xlsx"); 117 | GridExcel.readByEventModel(resourceAsStream,TradeOrder.class,ExcelType.XLSX) 118 | .window(2,ts -> System.out.println(JSON.toJSONString(ts)))//推荐在这里执行自己的业务逻辑 119 | .process(cs ->{ 120 | TradeOrder tradeOrder = new TradeOrder(); 121 | tradeOrder.setTradeOrderId(Long.valueOf(cs.get(0))); 122 | Consultant consultant = new Consultant(); 123 | consultant.setConsultantName(cs.get(3)); 124 | tradeOrder.setConsultant(consultant); 125 | tradeOrder.setPaymentRatio(cs.get(16)); 126 | return tradeOrder; 127 | },1); 128 | } 129 | /** 130 | * 使用Streaming UserModel写出数据到Excel 131 | * @throws Exception 132 | */ 133 | @Test 134 | public void writeExcelByStreaming() throws Exception { 135 | GridExcel.writeByStreaming(TradeOrder.class) 136 | .head(writeFunctionMap())//对象字段到Excel列的映射 137 | .createSheet() 138 | .process(MockData.data())//模拟数据。在这里设置业务数据集合。 139 | .write(FileUtils.openOutputStream(new File("/excel/test.xlsx"))); 140 | } 141 | ``` 142 | #### 无实体类读写Excel 143 | 由于没有自定义业务实体类,这里我们可以使用Map.class来代替。下面是读入Excel的例子,写Excel可以参照实现。 144 | ```java 145 | /** 146 | * No Entity无实体类读Excel文件 147 | * 业务逻辑处理方式三选一: 148 | * 1.启用windowListener,并将业务逻辑放在该函数中。 149 | * 2.不启用windowListener,使用get()方法取回全部数据集合,做后续处理。 150 | * 3.readFunction函数,直接放在函数中处理 或 使用final or effective final的局部变量存放这写数据,做后续处理。 151 | * 注意:使用EventModel时readFunction函数的输入为每行的cell值集合List。 152 | * @throws Exception 153 | */ 154 | @Test 155 | public void readXlsxByEventModelWithoutEntity() throws Exception { 156 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("2007.xlsx"); 157 | GridExcel.readByEventModel(resourceAsStream,Map.class,ExcelType.XLSX) 158 | .window(2,ts -> System.out.println(JSON.toJSONString(ts)))//推荐在这里执行自己的业务逻辑 159 | .process(cs ->{ 160 | Map map = new HashMap(); 161 | map.put("tradeOrderId",cs.get(0)); 162 | map.put("consultantName",cs.get(3)); 163 | map.put("paymentRatio",cs.get(16)); 164 | return map; 165 | },1); 166 | } 167 | ``` 168 | #### ReadExcel 169 | ##### ReadExcelByUserModel 170 | Use user model to read excel file. userModel —— 171 | - **缺点**:内存消耗大,会将excel信息全部加载到内存再进行处理。 172 | - **优点**:现成的API,使用和理解更简单。 173 | - **使用场景**:可以处理数据量较小的Excel。 174 | ##### ReadExcelByEventModel 175 | Use event model to read excel file. eventModel —— 176 | - **缺点**:没有现成的API,使用和理解较为复杂,适合中高级程序员(GridExcel的目标之一就是让EventModel的使用变得简单) 177 | - **优点**:非常小的内存占用,并没有在一开始就将所有内容加载到内存中,而是把主体内容的处理(存储,使用,丢弃)都交给了用户,用户可以自定义监听函数来处理这些内容。 178 | - **使用场景**:可以处理较大数据量的Excel,避免OOM和频繁FullGC 179 | #### WriteExcel 180 | ##### WriteExcelByUserModel 181 | Use user model to write excel file. userModel —— 182 | - **缺点**:会将产生的spreadsheets对象整个保存在内存中,所以write Excel的大小受到堆内存(Heap space)大小限制。 183 | - **优点**:使用和理解更简单。 184 | - **使用场景**:可以写出数据量较小的Excel。 185 | ##### WriteExcelByStreaming 186 | Use API-compatible streaming extension of XSSF to write very large excel file. streaming userModel—— 187 | - **缺点**: 188 | - 仅支持XSSF; 189 | - Sheet.clone() is not supported; 190 | - Formula evaluation is not supported; 191 | - Only a limited number of rows are accessible at a point in time. 192 | - **优点**:通过滑动窗口来实现,内存中只保留指定size of rows的内容,超出部分被写出到临时文件,write Excel的大小不再受到堆内存(Heap space)大小限制。 193 | - **使用场景**:可以写出非常大的Excel。 194 | #### Issues 195 | 在使用工具过程中出现问题,有功能添加或改动需求的可以向作者提Issue:https://github.com/liuhuagui/gridexcel/issues 196 | - 比如说,想要增加对首行以外的行列做样式扩展 197 | 198 | 可关注作者公众号 199 | ![在这里插入图片描述](https://raw.githubusercontent.com/liuhuagui/gridexcel/master/qrcode.jpg) -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.github.liuhuagui 8 | gridexcel 9 | 2.3 10 | gridexcel 11 | Universal solution for reading and writing simply Excel based on functional programming and POI 12 | EventModel. 13 | 14 | https://github.com/liuhuagui/gridexcel 15 | 16 | 17 | 18 | The Apache Software License, Version 2.0 19 | https://www.apache.org/licenses/LICENSE-2.0.txt 20 | repo 21 | 22 | 23 | 24 | 25 | 26 | lihuagui 27 | 799600902@qq.com 28 | 29 | Owner 30 | Founder 31 | Committer 32 | 33 | https://github.com/liuhuagui 34 | 35 | 36 | 37 | 38 | https://github.com/liuhuagui/gridexcel 39 | scm:git:git@github.com:liuhuagui/gridexcel.git 40 | scm:git:git@github.com:liuhuagui/gridexcel.git 41 | 42 | 43 | 44 | UTF-8 45 | 4.1.2 46 | 4.1.2 47 | 48 | 49 | 50 | 51 | org.apache.poi 52 | poi-ooxml 53 | ${poi-ooxml} 54 | 55 | 56 | org.apache.poi 57 | poi 58 | ${poi} 59 | 60 | 61 | commons-io 62 | commons-io 63 | 2.6 64 | 65 | 66 | org.slf4j 67 | slf4j-api 68 | 1.7.30 69 | 70 | 71 | 72 | org.slf4j 73 | slf4j-simple 74 | 1.7.30 75 | test 76 | 77 | 78 | com.alibaba 79 | fastjson 80 | 1.2.66 81 | test 82 | 83 | 84 | junit 85 | junit 86 | 4.12 87 | test 88 | 89 | 90 | 91 | 92 | 93 | 94 | maven-compiler-plugin 95 | 3.8.1 96 | 97 | 1.8 98 | 1.8 99 | 100 | 101 | 102 | maven-source-plugin 103 | 3.0.1 104 | 105 | true 106 | 107 | 108 | 109 | attach-sources 110 | 111 | jar-no-fork 112 | 113 | 114 | 115 | 116 | 117 | org.apache.maven.plugins 118 | maven-javadoc-plugin 119 | 3.0.0 120 | 121 | none 122 | 123 | 124 | 125 | attach-javadocs 126 | 127 | jar 128 | 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-gpg-plugin 135 | 1.6 136 | 137 | 138 | sign-artifacts 139 | verify 140 | 141 | sign 142 | 143 | 144 | 145 | --pinentry-mode 146 | loopback 147 | 148 | 149 | 150 | 151 | 152 | 153 | org.sonatype.plugins 154 | nexus-staging-maven-plugin 155 | 1.6.8 156 | true 157 | 158 | ossrh 159 | https://oss.sonatype.org/ 160 | true 161 | 162 | 163 | 164 | 165 | 166 | 167 | GitHub Issue Management 168 | https://github.com/liuhuagui/gridexcel/issues 169 | 170 | 171 | 172 | 173 | ossrh 174 | https://oss.sonatype.org/content/repositories/snapshots 175 | 176 | 177 | ossrh 178 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 179 | 180 | 181 | -------------------------------------------------------------------------------- /qrcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuagui/gridexcel/8ea1be715ecd2b92524f2b76a1e9ba27f702c5a0/qrcode.jpg -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/GridExcel.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel; 2 | 3 | import com.github.liuhuagui.gridexcel.eventmodel.EventModel; 4 | import com.github.liuhuagui.gridexcel.usermodel.read.ReadExcelByEventModel; 5 | import com.github.liuhuagui.gridexcel.usermodel.read.ReadExcelByUserModel; 6 | import com.github.liuhuagui.gridexcel.usermodel.write.WriteExcelByStreaming; 7 | import com.github.liuhuagui.gridexcel.usermodel.write.WriteExcelByUserModel; 8 | import com.github.liuhuagui.gridexcel.util.ExcelType; 9 | import org.apache.poi.openxml4j.exceptions.OpenXML4JException; 10 | import org.apache.poi.ss.usermodel.Workbook; 11 | import org.apache.poi.xssf.streaming.SXSSFWorkbook; 12 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; 13 | 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | 17 | /** 18 | * 统一标记。基于函数编程来实现,算法设计上类似于CSS中的Grid布局(先设置好布局再放置元素),所以叫做“GridExcel”。
19 | * 20 | * @author KaiKang 21 | */ 22 | public class GridExcel { 23 | /** 24 | * Use {@link Workbook} to create Excel object. 25 | * 26 | * @param workbook 27 | * @param tClass 28 | * @return 29 | */ 30 | public static ReadExcelByUserModel readByUserModel(Workbook workbook, Class tClass) { 31 | return ReadExcelByUserModel.excel(workbook, tClass); 32 | } 33 | 34 | /** 35 | * 根据文件名与Excel输入流创建ReadExcel对象 36 | * 37 | * @param stream 输入流 38 | * @param tClass 数据对象 39 | * @param excelType Excel类型 40 | * @return 41 | * @throws IOException 42 | */ 43 | public static ReadExcelByUserModel readByUserModel(InputStream stream, Class tClass, ExcelType excelType) throws IOException { 44 | return ReadExcelByUserModel.excel(stream, tClass, excelType); 45 | } 46 | 47 | /** 48 | * Use {@link EventModel} to create Excel object. 49 | * 50 | * @param eventModel 51 | * @param tClass 52 | * @return 53 | */ 54 | public static ReadExcelByEventModel readByEventModel(EventModel eventModel, Class tClass) { 55 | return ReadExcelByEventModel.excel(eventModel, tClass); 56 | } 57 | 58 | /** 59 | * 根据文件名与Excel输入流创建ReadExcel对象 60 | * 61 | * @param stream 输入流 62 | * @param tClass 数据对象 63 | * @param excelType Excel类型 64 | * @return 65 | * @throws IOException 66 | * @throws OpenXML4JException 67 | */ 68 | public static ReadExcelByEventModel readByEventModel(InputStream stream, Class tClass, ExcelType excelType) throws IOException, OpenXML4JException { 69 | return ReadExcelByEventModel.excel(stream, tClass, excelType); 70 | } 71 | 72 | /** 73 | * 使用{@link Workbook} 创建Excel对象 74 | * 75 | * @param workbook 76 | * @param tClass 77 | * @return 78 | */ 79 | public static WriteExcelByUserModel writeByUserModel(Workbook workbook, Class tClass) { 80 | return WriteExcelByUserModel.excel(workbook, tClass); 81 | } 82 | 83 | /** 84 | * 使用{@link XSSFWorkbook} 创建WriteExcel对象。 85 | * 86 | * @param tClass 87 | * @return 88 | */ 89 | public static WriteExcelByUserModel writeByUserModel(Class tClass) { 90 | return WriteExcelByUserModel.excel(tClass); 91 | } 92 | 93 | /** 94 | * 使用{@link Workbook} 创建WriteExcel对象 95 | * 96 | * @param workbook 97 | * @param tClass 98 | * @return 99 | */ 100 | public static WriteExcelByStreaming writeByStreaming(Workbook workbook, Class tClass) { 101 | return WriteExcelByStreaming.excel(workbook, tClass); 102 | } 103 | 104 | /** 105 | * 使用{@link SXSSFWorkbook} 创建WriteExcel对象。使用Streaming Usermodel API去Write出数据到Excel,降低内存占用。 106 | * 107 | * @param tClass 108 | * @return 109 | */ 110 | public static WriteExcelByStreaming writeByStreaming(Class tClass) { 111 | return WriteExcelByStreaming.excel(tClass); 112 | } 113 | 114 | /** 115 | * 使用{@link SXSSFWorkbook} 创建WriteExcel对象。使用Streaming Usermodel API去Write出数据到Excel,降低内存占用。 116 | * 117 | * @param rowAccessWindowSize the number of rows that are kept in memory until flushed out, see {@link SXSSFWorkbook}. 118 | * @param tClass 119 | * @return 120 | */ 121 | public static WriteExcelByStreaming writeByStreaming(int rowAccessWindowSize, Class tClass) { 122 | return WriteExcelByStreaming.excel(rowAccessWindowSize, tClass); 123 | } 124 | 125 | /** 126 | * 使用{@link SXSSFWorkbook} 创建WriteExcel对象。使用Streaming Usermodel API去Write出数据到Excel,降低内存占用。 127 | * 128 | * @param workbook the number of rows that are kept in memory until flushed out, see {@link SXSSFWorkbook}. 129 | * @param tClass 130 | * @return 131 | */ 132 | public static WriteExcelByStreaming writeByStreaming(XSSFWorkbook workbook, Class tClass) { 133 | return WriteExcelByStreaming.excel(workbook, tClass); 134 | } 135 | 136 | /** 137 | * 使用{@link SXSSFWorkbook} 创建WriteExcel对象。使用Streaming Usermodel API去Write出数据到Excel,降低内存占用。 138 | * 139 | * @param workbook 140 | * @param rowAccessWindowSize the number of rows that are kept in memory until flushed out, see {@link SXSSFWorkbook}. 141 | * @param tClass 142 | * @return 143 | */ 144 | public static WriteExcelByStreaming writeByStreaming(XSSFWorkbook workbook, int rowAccessWindowSize, Class tClass) { 145 | return WriteExcelByStreaming.excel(workbook, rowAccessWindowSize, tClass); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/bean/CellListOfRow.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.bean; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * A special list wrapper for handling @{@link ArrayIndexOutOfBoundsException} due to Missing Cells at the end of row. 7 | * 8 | * @author liuhuagui 9 | */ 10 | public class CellListOfRow { 11 | private List baseList; 12 | 13 | /** 14 | * returning the value instead of throwing @{@link ArrayIndexOutOfBoundsException}. 15 | */ 16 | private T defaultValue; 17 | 18 | public CellListOfRow(List baseList, T defaultValue) { 19 | this.baseList = baseList; 20 | this.defaultValue = defaultValue; 21 | } 22 | 23 | public T get(int index) { 24 | return index >= baseList.size() ? defaultValue : baseList.get(index); 25 | } 26 | 27 | public List baseList() { 28 | return baseList; 29 | } 30 | 31 | public int size() { 32 | return baseList.size(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/eventmodel/EventModel.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.eventmodel; 2 | 3 | import com.github.liuhuagui.gridexcel.bean.CellListOfRow; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.function.Consumer; 8 | 9 | /** 10 | * Use POI Event API to read excel,The advantage provided is that you can read a excel file with a relatively small memory footprint. 11 | * 12 | * @author KaiKang 13 | */ 14 | public abstract class EventModel { 15 | /** 16 | * the starting line number as a condition on enabling consumer function, 0-based 17 | */ 18 | protected int startRow; 19 | /** 20 | * Each time a row of data is read, the function interface is called to execute the custom logic. 21 | */ 22 | protected Consumer> readConsumer; 23 | /** 24 | * Store record values that appear in a row of cells 25 | */ 26 | protected List rowCellValues; 27 | 28 | public EventModel(int startRow, Consumer> readConsumer) { 29 | this.startRow = startRow; 30 | this.readConsumer = readConsumer; 31 | this.rowCellValues = new ArrayList(); 32 | } 33 | 34 | /** 35 | * Initiates the processing of the Excel file to Bean. 36 | * 37 | * @throws Exception 38 | */ 39 | public abstract void process() throws Exception; 40 | 41 | /** 42 | * 启用消费函数的起始行 43 | * 44 | * @param startRow 45 | * @return 46 | */ 47 | public EventModel startRow(int startRow) { 48 | this.startRow = startRow; 49 | return this; 50 | } 51 | 52 | /** 53 | * 设置消费函数。每读完一行记录(one row of records),调用该函数。 54 | * 55 | * @param readConsumer 56 | * @return 57 | */ 58 | public EventModel readConsumer(Consumer> readConsumer) { 59 | this.readConsumer = readConsumer; 60 | return this; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/eventmodel/XLSEventModel.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.eventmodel; 2 | 3 | import com.github.liuhuagui.gridexcel.bean.CellListOfRow; 4 | import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener; 5 | import org.apache.poi.hssf.eventusermodel.*; 6 | import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord; 7 | import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord; 8 | import org.apache.poi.hssf.model.HSSFFormulaParser; 9 | import org.apache.poi.hssf.record.*; 10 | import org.apache.poi.hssf.usermodel.HSSFWorkbook; 11 | import org.apache.poi.poifs.filesystem.POIFSFileSystem; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import java.io.IOException; 16 | import java.io.InputStream; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | import java.util.function.Consumer; 20 | 21 | /** 22 | * A XLS -> Bean processor, The advantage provided is that you can read an XLS with a relatively small memory footprint. 23 | * that uses the MissingRecordAware EventModel code to ensure it outputs all columns and rows. Basing on POI examples 24 | * https://poi.apache.org/components/spreadsheet/how-to.html 25 | * 26 | * @author KaiKang 27 | */ 28 | public class XLSEventModel extends EventModel implements HSSFListener { 29 | private static Logger log = LoggerFactory.getLogger(XLSEventModel.class); 30 | private POIFSFileSystem fs; 31 | 32 | /** 33 | * Should we output the formula, or the value it has? 34 | */ 35 | private boolean outputFormulaValues = true; 36 | 37 | /** 38 | * For parsing Formulas 39 | */ 40 | private SheetRecordCollectingListener workbookBuildingListener; 41 | private HSSFWorkbook stubWorkbook; 42 | 43 | // Records we pick up as we process 44 | private SSTRecord sstRecord; 45 | private FormatTrackingHSSFListener formatListener; 46 | 47 | /** 48 | * So we known which sheet we're on 49 | */ 50 | private int sheetIndex = -1; 51 | private BoundSheetRecord[] orderedBSRs; 52 | private List boundSheetRecords = new ArrayList<>(); 53 | 54 | // For handling formulas with string results 55 | private boolean outputNextStringRecord; 56 | 57 | /** 58 | * Creates a new XLS -> Bean converter 59 | * 60 | * @param fs The POIFSFileSystem to process 61 | * @param startRow the starting line number as a condition on enabling consumer function, 0-based 62 | * @param readConsumer Each time a row of data is read, the function interface is called to execute the custom logic. 63 | */ 64 | public XLSEventModel(POIFSFileSystem fs, int startRow, Consumer> readConsumer) { 65 | super(startRow, readConsumer); 66 | this.fs = fs; 67 | } 68 | 69 | /** 70 | * Creates a new XLS -> Bean converter 71 | * 72 | * @param stream the input stream of .xlsx file. 73 | * @param startRow the starting line number as a condition on enabling consumer function, 0-based 74 | * @param readConsumer Each time a row of data is read, the function interface is called to execute the custom logic. 75 | * @throws IOException 76 | */ 77 | public XLSEventModel(InputStream stream, int startRow, Consumer> readConsumer) throws IOException { 78 | this(new POIFSFileSystem(stream), startRow, readConsumer); 79 | } 80 | 81 | /** 82 | * Creates a new XLS -> Bean converter,default startRow=0 83 | * 84 | * @param stream the input stream of .xlsx file. 85 | * @param readConsumer Each time a row of data is read, the function interface is called to execute the custom logic. 86 | * @throws IOException 87 | */ 88 | public XLSEventModel(InputStream stream, Consumer> readConsumer) throws IOException { 89 | this(stream, 0, readConsumer); 90 | } 91 | 92 | /** 93 | * Creates a new XLS -> Bean converter,default startRow=0,readConsumer=null 94 | * 95 | * @param stream the input stream of .xlsx file. 96 | * @throws IOException 97 | */ 98 | public XLSEventModel(InputStream stream) throws IOException { 99 | this(stream, null); 100 | } 101 | 102 | @Override 103 | public void process() throws IOException { 104 | MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this); 105 | formatListener = new FormatTrackingHSSFListener(listener); 106 | 107 | HSSFEventFactory factory = new HSSFEventFactory(); 108 | HSSFRequest request = new HSSFRequest(); 109 | 110 | if (outputFormulaValues) { 111 | request.addListenerForAllRecords(formatListener); 112 | } else { 113 | workbookBuildingListener = new SheetRecordCollectingListener(formatListener); 114 | request.addListenerForAllRecords(workbookBuildingListener); 115 | } 116 | 117 | factory.processWorkbookEvents(request, fs); 118 | } 119 | 120 | /** 121 | * Main HSSFListener method, processes events, and outputs the 122 | * Bean as the file is processed. 123 | */ 124 | @Override 125 | public void processRecord(Record record) { 126 | String thisStr = null; 127 | 128 | switch (record.getSid()) { 129 | case BoundSheetRecord.sid: 130 | boundSheetRecords.add((BoundSheetRecord) record); 131 | break; 132 | case BOFRecord.sid: 133 | BOFRecord br = (BOFRecord) record; 134 | if (br.getType() == BOFRecord.TYPE_WORKSHEET) { 135 | // Create sub workbook if required 136 | if (workbookBuildingListener != null && stubWorkbook == null) { 137 | stubWorkbook = workbookBuildingListener.getStubHSSFWorkbook(); 138 | } 139 | 140 | // Output the worksheet name 141 | // Works by ordering the BSRs by the location of 142 | // their BOFRecords, and then knowing that we 143 | // process BOFRecords in byte offset order 144 | sheetIndex++; 145 | if (orderedBSRs == null) { 146 | orderedBSRs = BoundSheetRecord.orderByBofPosition(boundSheetRecords); 147 | } 148 | log.info(orderedBSRs[sheetIndex].getSheetname()); 149 | } 150 | break; 151 | 152 | case SSTRecord.sid: 153 | sstRecord = (SSTRecord) record; 154 | break; 155 | 156 | case BlankRecord.sid: 157 | // BlankRecord brec = (BlankRecord) record; 158 | thisStr = ""; 159 | break; 160 | case BoolErrRecord.sid: 161 | // BoolErrRecord berec = (BoolErrRecord) record; 162 | thisStr = ""; 163 | break; 164 | 165 | case FormulaRecord.sid: 166 | FormulaRecord frec = (FormulaRecord) record; 167 | if (outputFormulaValues) { 168 | if (Double.isNaN(frec.getValue())) { 169 | // Formula result is a string 170 | // This is stored in the next record 171 | outputNextStringRecord = true; 172 | } else { 173 | thisStr = formatListener.formatNumberDateCell(frec); 174 | } 175 | } else { 176 | thisStr = HSSFFormulaParser.toFormulaString(stubWorkbook, frec.getParsedExpression()); 177 | } 178 | break; 179 | case StringRecord.sid: 180 | if (outputNextStringRecord) { 181 | // String for formula 182 | StringRecord srec = (StringRecord) record; 183 | thisStr = srec.getString(); 184 | outputNextStringRecord = false; 185 | } 186 | break; 187 | 188 | case LabelRecord.sid: 189 | LabelRecord lrec = (LabelRecord) record; 190 | thisStr = lrec.getValue(); 191 | break; 192 | case LabelSSTRecord.sid: 193 | LabelSSTRecord lsrec = (LabelSSTRecord) record; 194 | if (sstRecord == null) { 195 | thisStr = "(No SST Record, can't identify string)"; 196 | } else { 197 | thisStr = sstRecord.getString(lsrec.getSSTIndex()).toString(); 198 | } 199 | break; 200 | case NoteRecord.sid: 201 | // NoteRecord nrec = (NoteRecord) record; 202 | // TODO: Find object to match nrec.getShapeId() 203 | thisStr = "(TODO)"; 204 | break; 205 | case NumberRecord.sid: 206 | NumberRecord numrec = (NumberRecord) record; 207 | // Format 208 | thisStr = formatListener.formatNumberDateCell(numrec); 209 | break; 210 | case RKRecord.sid: 211 | // RKRecord rkrec = (RKRecord) record; 212 | thisStr = "(TODO)"; 213 | break; 214 | default: 215 | break; 216 | } 217 | 218 | // Handle missing column 219 | if (record instanceof MissingCellDummyRecord) { 220 | // MissingCellDummyRecord mc = (MissingCellDummyRecord)record; 221 | thisStr = ""; 222 | } 223 | 224 | // If we got something to store it into rowCellValues. 225 | if (thisStr != null) { 226 | rowCellValues.add(thisStr.isEmpty() ? null : thisStr);//add null instead of missing cells. 227 | } 228 | 229 | // Handle end of row 230 | if (record instanceof LastCellOfRowDummyRecord) { 231 | // End the row 232 | //If the current line number≥ startRow, then the consumer function is enabled, 233 | //otherwise the entire line record is directly discarded and is not processed. 234 | if (((LastCellOfRowDummyRecord) record).getRow() >= startRow) { 235 | readConsumer.accept(new CellListOfRow<>(rowCellValues, null)); 236 | } 237 | // init new row 238 | rowCellValues = new ArrayList(); 239 | } 240 | } 241 | } -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/eventmodel/XLSXEventModel.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.eventmodel; 2 | 3 | import com.github.liuhuagui.gridexcel.bean.CellListOfRow; 4 | import org.apache.poi.ooxml.util.SAXHelper; 5 | import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 6 | import org.apache.poi.openxml4j.exceptions.OpenXML4JException; 7 | import org.apache.poi.openxml4j.opc.OPCPackage; 8 | import org.apache.poi.ss.usermodel.DataFormatter; 9 | import org.apache.poi.ss.util.CellAddress; 10 | import org.apache.poi.ss.util.CellReference; 11 | import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable; 12 | import org.apache.poi.xssf.eventusermodel.XSSFReader; 13 | import org.apache.poi.xssf.eventusermodel.XSSFReader.SheetIterator; 14 | import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler; 15 | import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler.SheetContentsHandler; 16 | import org.apache.poi.xssf.usermodel.XSSFComment; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import org.xml.sax.ContentHandler; 20 | import org.xml.sax.InputSource; 21 | import org.xml.sax.SAXException; 22 | import org.xml.sax.XMLReader; 23 | 24 | import javax.xml.parsers.ParserConfigurationException; 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.util.ArrayList; 28 | import java.util.Iterator; 29 | import java.util.function.Consumer; 30 | 31 | /** 32 | * A XLSX -> Bean processor, The advantage provided is that you can read an XLSX with a relatively small memory footprint. 33 | * Basing on POI examples. https://poi.apache.org/components/spreadsheet/how-to.html 34 | * 35 | * @author KaiKang 36 | */ 37 | public class XLSXEventModel extends EventModel { 38 | private static Logger log = LoggerFactory.getLogger(XLSXEventModel.class); 39 | private OPCPackage pkg; 40 | private XSSFReader reader; 41 | 42 | /** 43 | * Creates a new XLSX -> Bean converter 44 | * 45 | * @param pkg Represents a container that can store multiple data objects. 46 | * @param startRow the starting line number as a condition on enabling consumer function, 0-based 47 | * @param readConsumer Each time a row of data is read, the function interface is called to execute the custom logic. 48 | * @throws OpenXML4JException 49 | * @throws IOException 50 | */ 51 | public XLSXEventModel(OPCPackage pkg, int startRow, Consumer> readConsumer) throws IOException, OpenXML4JException { 52 | super(startRow, readConsumer); 53 | init(pkg); 54 | } 55 | 56 | /** 57 | * init relevant information 58 | */ 59 | public void init(OPCPackage pkg) throws IOException, OpenXML4JException { 60 | this.pkg = pkg; 61 | this.reader = new XSSFReader(pkg); 62 | } 63 | 64 | /** 65 | * Creates a new XLSX -> Bean converter 66 | * 67 | * @param stream the input stream of .xlsx file. 68 | * @param startRow the starting line number as a condition on enabling consumer function, 0-based 69 | * @param readConsumer Each time a row of data is read, the function interface is called to execute the custom logic. 70 | * @throws IOException 71 | * @throws OpenXML4JException 72 | */ 73 | public XLSXEventModel(InputStream stream, int startRow, Consumer> readConsumer) throws OpenXML4JException, IOException { 74 | this(OPCPackage.open(stream), startRow, readConsumer); 75 | } 76 | 77 | /** 78 | * Creates a new XLSX -> Bean converter,default startRow=0 79 | * 80 | * @param stream the input stream of .xlsx file. 81 | * @param readConsumer Each time a row of data is read, the function interface is called to execute the custom logic. 82 | * @throws OpenXML4JException 83 | * @throws IOException 84 | */ 85 | public XLSXEventModel(InputStream stream, Consumer> readConsumer) throws OpenXML4JException, IOException { 86 | this(stream, 0, readConsumer); 87 | } 88 | 89 | /** 90 | * Creates a new XLSX -> Bean converter,default startRow=0 91 | * 92 | * @param stream the input stream of .xlsx file. 93 | * @throws OpenXML4JException 94 | * @throws IOException 95 | */ 96 | public XLSXEventModel(InputStream stream) throws OpenXML4JException, IOException { 97 | this(stream, null); 98 | } 99 | 100 | /** 101 | * Processing input stream of all sheets. 102 | * 103 | * @throws Exception 104 | */ 105 | private void processAllSheets() throws SAXException, InvalidFormatException, ParserConfigurationException, IOException { 106 | XMLReader parser = createSheetParser(); 107 | Iterator sheets = reader.getSheetsData(); 108 | while (sheets.hasNext()) { 109 | InputStream sheet = sheets.next(); 110 | log.info("Processing new sheet: {}\n", ((SheetIterator) sheets).getSheetName()); 111 | InputSource sheetSource = new InputSource(sheet); 112 | parser.parse(sheetSource); 113 | sheet.close(); 114 | } 115 | } 116 | 117 | /** 118 | * Create a Parser that parses sheet. 119 | * 120 | * @return 121 | * @throws SAXException 122 | * @throws ParserConfigurationException 123 | * @throws InvalidFormatException 124 | * @throws IOException 125 | */ 126 | protected XMLReader createSheetParser() throws SAXException, ParserConfigurationException, InvalidFormatException, IOException { 127 | XMLReader parser = SAXHelper.newXMLReader(); 128 | ContentHandler handler = createSheetXMLHandler(); 129 | parser.setContentHandler(handler); 130 | return parser; 131 | } 132 | 133 | /** 134 | * Create a Handler that handles xml. 135 | * 136 | * @return 137 | * @throws IOException 138 | * @throws SAXException 139 | * @throws InvalidFormatException 140 | */ 141 | protected XSSFSheetXMLHandler createSheetXMLHandler() throws IOException, SAXException, InvalidFormatException { 142 | ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(this.pkg); 143 | DataFormatter formatter = new DataFormatter(); 144 | XSSFSheetXMLHandler handler = new XSSFSheetXMLHandler( 145 | reader.getStylesTable(), null, strings, createSheetContentsHandler(), formatter, false); 146 | return handler; 147 | } 148 | 149 | /** 150 | * Create a Handler that handles contents of sheet. 151 | * 152 | * @return 153 | */ 154 | protected SheetContentsHandler createSheetContentsHandler() { 155 | return new SheetContentsHandler() { 156 | private int currentRow = -1; 157 | private int currentCol = -1; 158 | 159 | @Override 160 | public void startRow(int rowNum) { 161 | // Prepare for this row 162 | currentRow = rowNum; 163 | currentCol = -1; 164 | } 165 | 166 | @Override 167 | public void endRow(int rowNum) { 168 | // End the row 169 | //If the current line number≥ startRow, then the consumer function is enabled, 170 | //otherwise the entire line record is directly discarded and is not processed. 171 | if (rowNum >= startRow) { 172 | readConsumer.accept(new CellListOfRow<>(rowCellValues, null)); 173 | } 174 | // init new row 175 | rowCellValues = new ArrayList(); 176 | } 177 | 178 | @Override 179 | public void cell(String cellReference, String formattedValue, XSSFComment comment) { 180 | // gracefully handle missing CellRef here in a similar way as XSSFCell does 181 | if (cellReference == null) { 182 | cellReference = new CellAddress(currentRow, currentCol).formatAsString(); 183 | } 184 | 185 | // Did we miss any cells? 186 | int thisCol = (new CellReference(cellReference)).getCol(); 187 | int missedCols = thisCol - currentCol - 1; 188 | for (int i = 0; i < missedCols; i++) { 189 | rowCellValues.add(null); 190 | } 191 | currentCol = thisCol; 192 | 193 | //add Non-null cell value. 194 | rowCellValues.add(formattedValue); 195 | } 196 | }; 197 | } 198 | 199 | @Override 200 | public void process() throws SAXException, InvalidFormatException, ParserConfigurationException, IOException { 201 | processAllSheets(); 202 | } 203 | } -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/usermodel/read/ReadExcel.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.usermodel.read; 2 | 3 | import com.github.liuhuagui.gridexcel.util.Assert; 4 | 5 | import java.io.IOException; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Objects; 9 | import java.util.function.Consumer; 10 | import java.util.function.Function; 11 | 12 | /** 13 | * Read excel file.采用控制窗口的方式读取Excel内容,当满足控制条件时执行自定义逻辑。 14 | * 15 | * @param 需要处理的数据 16 | * @param 返回的数据 17 | * @author KaiKang 18 | * @since JDK1.8 19 | */ 20 | public abstract class ReadExcel { 21 | /** 22 | * Read excel rows to the list object. 23 | */ 24 | protected List list; 25 | 26 | /** 27 | * When the data is read from Excel, the size of the control window of 0 means no control, and N(n∈N+) means that the listener function is executed as long as the size of list reaches N. 28 | *
Default 0,不做窗口控制,不启用监听函数 29 | */ 30 | protected int windowSize; 31 | 32 | /** 33 | * The listen function of the Read operation. Used to execute custom logic, for example, to promote GC and reduce memory.
34 | * 默认为空方法,表示什么都不做。 35 | */ 36 | protected Consumer> windowListener; 37 | 38 | public ReadExcel() { 39 | this.windowSize = 0;//不做窗口控制,不启用监听函数 40 | this.windowListener = ts -> { 41 | };//什么都不做 42 | } 43 | 44 | /** 45 | * 返回读入的Excel内容集合。如果监听函数被启用(windowSize设置为正整数),该方法将返回null。 46 | * 47 | * @return 48 | */ 49 | public List get() { 50 | return this.list; 51 | } 52 | 53 | /** 54 | *
    55 | *
  1. 设置从Excel读入数据时的控制窗口大小。0表示不控制大小,n(n∈N+)表示只要list中元素个数达到n,调用监听函数。
  2. 56 | *
  3. 设置窗口监听函数。The listen function of the Read operation. Used to execute custom logic, for example, to promote GC and reduce memory.
  4. 57 | *
  5. 如果监听函数被启用(windowSize设置为正整数),{@link #get()} 将返回null。
  6. 58 | *
59 | * 注意:该方法需要在 {@link #process(Function, int)} 之前调用 60 | * 61 | * @param windowSize 默认0,不做窗口控制,不启用监听函数 62 | * @param windowListener 默认为空方法,表示什么都不做。 63 | * @return 64 | */ 65 | public ReadExcel window(int windowSize, Consumer> windowListener) { 66 | this.windowSize = windowSize; 67 | this.windowListener = windowListener; 68 | return this; 69 | } 70 | 71 | /** 72 | * 处理读入的Excel数据流 73 | * 74 | * @param readFunction 读入Excel数据时需要调用的处理函数,表示如何处理 {@link S} 数据。输入是 {@link S},返回值是{@link T} 75 | * @param startNum 起始行,0-based 76 | * @return 77 | * @throws IOException 78 | */ 79 | public ReadExcel process(Function readFunction, int startNum) throws Exception { 80 | Assert.notNull(readFunction, "Parameter readFunction can't be null."); 81 | if (Objects.isNull(list)) { 82 | if (windowSize == 0)//不做大小控制 83 | list = new ArrayList(); 84 | else//使用窗口大小作为数组容量initialCapacity 85 | list = new ArrayList(windowSize); 86 | } 87 | return null; 88 | } 89 | 90 | /** 91 | * 调用监听函数,执行自定义逻辑 92 | */ 93 | protected void invokeListener() { 94 | //如果相等,windowSize一定不为0,所以windowSize=0,可以表示不启用监听函数。 95 | if (list.size() == windowSize) { 96 | //监听器执行自定义逻辑 97 | windowListener.accept(list); 98 | //重新初始化list,保证之前的数据被GC 99 | list = new ArrayList(windowSize); 100 | } 101 | } 102 | 103 | /** 104 | * Fixed a bug in version 2.2 that prevented the windowListener from processing all rows 105 | * from excel when the total number of rows could not be divided exactly by the windowSize.
106 | * Note:This method should be called at the end of the {@link #process(Function, int)} method 107 | */ 108 | protected void clearList() { 109 | if (windowSize > 0 && list.size() > 0) { 110 | //监听器执行自定义逻辑 111 | windowListener.accept(list); 112 | //执行到这里,excel已经被全部读取, 113 | //将list赋值null(促使list在this失活前不可达,可提前回收这部分内存) 114 | list = null; 115 | } 116 | } 117 | 118 | } -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/usermodel/read/ReadExcelByEventModel.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.usermodel.read; 2 | 3 | import com.github.liuhuagui.gridexcel.bean.CellListOfRow; 4 | import com.github.liuhuagui.gridexcel.eventmodel.EventModel; 5 | import com.github.liuhuagui.gridexcel.eventmodel.XLSEventModel; 6 | import com.github.liuhuagui.gridexcel.eventmodel.XLSXEventModel; 7 | import com.github.liuhuagui.gridexcel.util.ExcelType; 8 | import org.apache.poi.openxml4j.exceptions.OpenXML4JException; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.util.function.Consumer; 13 | import java.util.function.Function; 14 | 15 | /** 16 | * Use event model to read excel file.
17 | * eventModel —— 18 | *
    19 | *
  • 缺点:没有现成的API,使用和理解较为复杂,适合中高级程序员(GridExcel的目标之一就是让EventModel的使用变得简单)
  • 20 | *
  • 优点:非常小的内存占用,并没有在一开始就将所有内容加载到内存中,而是把主体内容的处理(存储,使用,丢弃)都交给了用户,用户可以自定义监听函数来处理这些内容。
  • 21 | *
  • 使用场景:可以处理较大数据量的Excel,避免OOM和频繁FullGC
  • 22 | *
23 | * 24 | * @author KaiKangØ 25 | * @since JDK1.8 26 | */ 27 | public class ReadExcelByEventModel extends ReadExcel, T> { 28 | /** 29 | * The model of using POI Event API to read excel. 30 | */ 31 | private EventModel eventModel; 32 | 33 | public ReadExcelByEventModel(EventModel eventModel) { 34 | super(); 35 | this.eventModel = eventModel; 36 | } 37 | 38 | /** 39 | * Use {@link EventModel} to create Excel object. 40 | * 41 | * @param eventModel 42 | * @param tClass 43 | * @return 44 | */ 45 | public static ReadExcelByEventModel excel(EventModel eventModel, Class tClass) { 46 | return new ReadExcelByEventModel(eventModel); 47 | } 48 | 49 | /** 50 | * 根据文件名与Excel输入流创建ReadExcel对象 51 | * 52 | * @param stream 输入流 53 | * @param tClass 数据对象 54 | * @param excelType Excel类型 55 | * @return 56 | * @throws IOException 57 | * @throws OpenXML4JException 58 | */ 59 | public static ReadExcelByEventModel excel(InputStream stream, Class tClass, ExcelType excelType) throws IOException, OpenXML4JException { 60 | if (excelType == ExcelType.XLS) 61 | return excel(new XLSEventModel(stream), tClass); 62 | if (excelType == ExcelType.XLSX) 63 | return excel(new XLSXEventModel(stream), tClass); 64 | throw new IllegalArgumentException("The GridExcel only supports .xls or .xlsx"); 65 | } 66 | 67 | @Override 68 | public ReadExcelByEventModel process(Function, T> readFunction, int startNum) throws Exception { 69 | super.process(readFunction, startNum); 70 | //每读完一行记录(one row of records),调用该函数 71 | Consumer> readConsumer = cells -> { 72 | list.add(readFunction.apply(cells)); 73 | //调用监听函数,执行自定义逻辑 74 | invokeListener(); 75 | }; 76 | eventModel.startRow(startNum) 77 | .readConsumer(readConsumer) 78 | .process(); 79 | clearList(); 80 | return this; 81 | } 82 | } -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/usermodel/read/ReadExcelByUserModel.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.usermodel.read; 2 | 3 | import com.github.liuhuagui.gridexcel.util.ExcelType; 4 | import org.apache.poi.hssf.usermodel.HSSFWorkbook; 5 | import org.apache.poi.ss.usermodel.Row; 6 | import org.apache.poi.ss.usermodel.Sheet; 7 | import org.apache.poi.ss.usermodel.Workbook; 8 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.util.function.Function; 13 | 14 | /** 15 | * Use user model to read excel file.
16 | * userModel —— 17 | *
    18 | *
  • 缺点:内存消耗大,会将excel信息全部加载到内存再进行处理。
  • 19 | *
  • 优点:现成的API,使用和理解更简单。
  • 20 | *
  • 使用场景:可以处理数据量较小的Excel。
  • 21 | *
22 | * 23 | * @author KaiKang 24 | * @since JDK1.8 25 | */ 26 | public class ReadExcelByUserModel extends ReadExcel { 27 | /** 28 | * High level representation of a Excel workbook. 29 | */ 30 | private Workbook workbook; 31 | 32 | public ReadExcelByUserModel(Workbook workbook) { 33 | super(); 34 | this.workbook = workbook; 35 | } 36 | 37 | /** 38 | * Use {@link Workbook} to create Excel object. 39 | * 40 | * @param workbook 41 | * @param tClass 42 | * @return 43 | */ 44 | public static ReadExcelByUserModel excel(Workbook workbook, Class tClass) { 45 | return new ReadExcelByUserModel(workbook); 46 | } 47 | 48 | /** 49 | * 根据文件名与Excel输入流创建ReadExcel对象 50 | * 51 | * @param stream 输入流 52 | * @param tClass 数据对象 53 | * @param excelType Excel类型 54 | * @return 55 | * @throws IOException 56 | */ 57 | public static ReadExcelByUserModel excel(InputStream stream, Class tClass, ExcelType excelType) throws IOException { 58 | if (excelType == ExcelType.XLS) 59 | return excel(new HSSFWorkbook(stream), tClass); 60 | if (excelType == ExcelType.XLSX) 61 | return excel(new XSSFWorkbook(stream), tClass); 62 | throw new IllegalArgumentException("The GridExcel only supports .xls or .xlsx"); 63 | } 64 | 65 | @Override 66 | public ReadExcelByUserModel process(Function readFunction, int startNum) throws Exception { 67 | super.process(readFunction, startNum); 68 | // Read the Sheet 69 | for (int numSheet = 0; numSheet < workbook.getNumberOfSheets(); numSheet++) { 70 | Sheet sheet = workbook.getSheetAt(numSheet); 71 | if (sheet == null) 72 | continue; 73 | // Read the Row 74 | for (int rowNum = startNum; rowNum <= sheet.getLastRowNum(); rowNum++) { 75 | Row row = sheet.getRow(rowNum); 76 | if (row == null) 77 | continue; 78 | list.add(readFunction.apply(row)); 79 | //调用监听函数,执行自定义逻辑 80 | invokeListener(); 81 | } 82 | } 83 | workbook.close(); 84 | clearList(); 85 | return this; 86 | } 87 | } -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/usermodel/write/WriteExcel.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.usermodel.write; 2 | 3 | import com.github.liuhuagui.gridexcel.util.Assert; 4 | import org.apache.poi.ss.usermodel.*; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | import java.util.HashMap; 12 | import java.util.LinkedHashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.function.Function; 16 | 17 | /** 18 | * Write excel file.
19 | * @author KaiKang 20 | * @since JDK1.8 21 | */ 22 | public abstract class WriteExcel{ 23 | private static Logger log = LoggerFactory.getLogger(WriteExcel.class); 24 | /**实际的Excel对象*/ 25 | private Workbook workbook; 26 | /**当前正在处理的Sheet*/ 27 | private Sheet currentSheet; 28 | /**代替null与异常填充到单元格的符号*/ 29 | private String fill; 30 | 31 | /**是否拥有首行*/ 32 | private boolean hasHead; 33 | /**对于整个workbook中所有Sheet默认的首行样式*/ 34 | private CellStyle defaultHeadStyle; 35 | /**Sheet和首行样式的映射*/ 36 | private Map headStyles; 37 | 38 | /**将数据集合写出到Excel,每列数据对应的写出方式(函数接口,对象字段到Excel列的映射),为了保证处理顺序,使用{@link LinkedHashMap}*/ 39 | private LinkedHashMap> writeFunctionMap; 40 | 41 | public WriteExcel(Workbook workbook) { 42 | this.workbook = workbook; 43 | this.hasHead = false;//默认没有首行 44 | this.headStyles = new HashMap(); 45 | this.fill = " - "; 46 | } 47 | 48 | /** 49 | * 设置填充信息,默认为“ - ” 50 | * @param fill 51 | * @return 52 | */ 53 | public WriteExcel fill(String fill) { 54 | this.fill = fill; 55 | return this; 56 | } 57 | 58 | /** 59 | * 创建一个新的Sheet,sheetName默认为Sheet+ sheets.size() 60 | * @return 61 | */ 62 | public WriteExcel createSheet() { 63 | this.currentSheet = workbook.createSheet(); 64 | return this; 65 | } 66 | 67 | /**创一个名为sheetName的Sheet 68 | * @param sheetName 69 | * @return 70 | */ 71 | public WriteExcel createSheet(String sheetName) { 72 | this.currentSheet = workbook.createSheet(sheetName); 73 | return this; 74 | } 75 | 76 | /** 77 | * 复制索引为sheetIndex的sheet(新的sheet) 78 | * @param sheetIndex 79 | * @return 80 | */ 81 | public WriteExcel cloneSheet(int sheetIndex) { 82 | this.currentSheet = workbook.cloneSheet(sheetIndex); 83 | return this; 84 | } 85 | 86 | /** 87 | * 取出索引为sheetIndex的sheet作为当前sheet 88 | * @param sheetIndex 89 | * @return 90 | */ 91 | public WriteExcel sheet(int sheetIndex) { 92 | this.currentSheet = workbook.getSheetAt(sheetIndex); 93 | return this; 94 | } 95 | 96 | /** 97 | * 取出名为sheetName的sheet作为当前sheet 98 | * @param sheetName 99 | * @return 100 | */ 101 | public WriteExcel sheet(String sheetName) { 102 | this.currentSheet = workbook.getSheet(sheetName); 103 | return this; 104 | } 105 | 106 | /** 107 | * 为当前sheet设置首行样式headStyle 108 | * @param styleFunction 一个函数接口,输入Workbook,返回自定义CellStyle 109 | * @return 110 | */ 111 | public WriteExcel headStyle(Function styleFunction) { 112 | headStyles.put(currentSheet, styleFunction.apply(workbook)); 113 | return this; 114 | } 115 | 116 | /** 117 | * 没有首行的解析方式。此时writeFunctionMap的Key可为任意不重复的值,建议设置为阿拉伯数字。 118 | *
为了保证处理顺序,使用{@link LinkedHashMap} 119 | * @param writeFunctionMap 对象字段到Excel列的映射 120 | * @return 121 | */ 122 | public WriteExcel noHead(LinkedHashMap> writeFunctionMap) { 123 | this.hasHead = false; 124 | this.writeFunctionMap = writeFunctionMap; 125 | return this; 126 | } 127 | 128 | /** 129 | * 拥有首行的解析方式。此时writeFunctionMap的Key会用作首行的列名 130 | *
为了保证处理顺序,使用{@link LinkedHashMap} 131 | * @param writeFunctionMap 对象字段到Excel列的映射 132 | * @return 133 | */ 134 | public WriteExcel head(LinkedHashMap> writeFunctionMap) { 135 | this.hasHead = true; 136 | this.writeFunctionMap = writeFunctionMap; 137 | return this; 138 | } 139 | 140 | /** 141 | * 将数据集合写出到Excel 142 | * @return 143 | */ 144 | public WriteExcel process(List list) { 145 | Assert.notEmpty(list,"The list is null or empty."); 146 | Assert.notNull(currentSheet, "The currentSheet is null"); 147 | //用来创建行,如果已有首行,j=1 148 | int j = 0; 149 | //创建首行 150 | if(hasHead){ 151 | Row headRow = currentSheet.createRow(0); 152 | CellStyle headStyle = headCellStyle(); 153 | //创建首行 154 | int i = 0; 155 | for(String name : writeFunctionMap.keySet()){ 156 | Cell headCell = headRow.createCell(i++); 157 | headCell.setCellValue(name); 158 | headCell.setCellStyle(headStyle); 159 | } 160 | j = 1;//如果已有首行,j=1 161 | } 162 | 163 | //创建其余行 164 | for(T t : list){ 165 | Row createRow = currentSheet.createRow(j++); 166 | int i = 0; 167 | for(Function function : writeFunctionMap.values()){ 168 | createRow.createCell(i++).setCellValue(fillCellValue(function, t)); 169 | } 170 | } 171 | 172 | return this; 173 | } 174 | 175 | /** 176 | * 写出Excel到某个数据流 177 | * @param stream 178 | * @throws IOException 179 | */ 180 | public void write(OutputStream stream) throws IOException { 181 | this.workbook.write(stream); 182 | } 183 | 184 | /** 185 | * 将Excel写出至字节数组 186 | * @return 187 | * @throws IOException 188 | */ 189 | public byte[] writeBytes() throws IOException { 190 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 191 | workbook.write(os); 192 | workbook.close(); 193 | return os.toByteArray(); 194 | } 195 | 196 | /** 197 | * 创建表格头Cell样式。可由子类复写 198 | * @return 199 | */ 200 | protected CellStyle headCellStyle() { 201 | //如果自定义了,返回自定义的cellStyle 202 | CellStyle cellStyle0 = headStyles.get(currentSheet); 203 | if(cellStyle0 != null) 204 | return cellStyle0; 205 | 206 | //如果没有自定义,则返回默认样式 207 | if(defaultHeadStyle != null) 208 | return defaultHeadStyle; 209 | 210 | //如果也没有默认样式,创建默认样式,并存下来允许所有Sheet使用 211 | CellStyle cellStyle = workbook.createCellStyle(); 212 | Font font = workbook.createFont(); 213 | font.setBold(true); 214 | cellStyle.setFont(font); 215 | 216 | cellStyle.setAlignment(HorizontalAlignment.CENTER); 217 | cellStyle.setBorderBottom(BorderStyle.THIN); 218 | cellStyle.setBorderLeft(BorderStyle.THIN); 219 | cellStyle.setBorderRight(BorderStyle.THIN); 220 | cellStyle.setBorderTop(BorderStyle.THIN); 221 | cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); 222 | cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); 223 | cellStyle.setBottomBorderColor((short) 0x000000); 224 | cellStyle.setTopBorderColor((short) 0x000000); 225 | cellStyle.setLeftBorderColor((short) 0x000000); 226 | cellStyle.setRightBorderColor((short) 0x000000); 227 | 228 | this.defaultHeadStyle = cellStyle; 229 | return cellStyle; 230 | } 231 | 232 | /** 233 | * 如果函数接口function,调用返回值为null或抛出异常,则使用fill符号来填充表格Cell。可由子类复写 234 | * @param function 函数接口 235 | * @param t 数据对象 236 | * @return 填充符号 237 | */ 238 | protected String fillCellValue(Function function,T t) { 239 | try { 240 | Object apply = function.apply(t); 241 | if(apply != null) 242 | return String.valueOf(apply); 243 | } catch (Exception e) { 244 | log.error(e.getMessage()); 245 | } 246 | return fill; 247 | } 248 | } -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/usermodel/write/WriteExcelByStreaming.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.usermodel.write; 2 | 3 | import org.apache.poi.ss.usermodel.Workbook; 4 | import org.apache.poi.xssf.streaming.SXSSFWorkbook; 5 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; 6 | 7 | /** 8 | * Use API-compatible streaming extension of XSSF to write very large excel file.
9 | * streaming userModel—— 10 | *
    11 | *
  • 缺点:
      12 | *
    1. 仅支持XSSF;
    2. 13 | *
    3. Sheet.clone() is not supported;
    4. 14 | *
    5. Formula evaluation is not supported;
    6. 15 | *
    7. Only a limited number of rows are accessible at a point in time.
    8. 16 | *
  • 17 | *
  • 优点:通过滑动窗口来实现,内存中只保留指定size of rows的内容,超出部分被写出到临时文件,write Excel的大小不再受到堆内存(Heap space)大小限制。
  • 18 | *
  • 使用场景:可以写出非常大的Excel。
  • 19 | *
20 | * @author KaiKang 21 | * @since JDK1.8 22 | */ 23 | public class WriteExcelByStreaming extends WriteExcel{ 24 | public WriteExcelByStreaming(Workbook workbook) { 25 | super(workbook); 26 | } 27 | 28 | /** 29 | * 使用{@link Workbook} 创建WriteExcel对象 30 | * @param workbook 31 | * @param tClass 32 | * @return 33 | */ 34 | public static WriteExcelByStreaming excel(Workbook workbook, Class tClass) { 35 | return new WriteExcelByStreaming(workbook); 36 | } 37 | 38 | /** 39 | * 使用{@link SXSSFWorkbook} 创建WriteExcel对象。使用Streaming Usermodel API去Write出数据到Excel,降低内存占用。 40 | * @param tClass 41 | * @return 42 | */ 43 | public static WriteExcelByStreaming excel(Class tClass) { 44 | return excel(new SXSSFWorkbook(), tClass); 45 | } 46 | 47 | /** 48 | * 使用{@link SXSSFWorkbook} 创建WriteExcel对象。使用Streaming Usermodel API去Write出数据到Excel,降低内存占用。 49 | * @param rowAccessWindowSize the number of rows that are kept in memory until flushed out, see {@link SXSSFWorkbook}. 50 | * @param tClass 51 | * @return 52 | */ 53 | public static WriteExcelByStreaming excel(int rowAccessWindowSize, Class tClass) { 54 | return excel(new SXSSFWorkbook(rowAccessWindowSize), tClass); 55 | } 56 | 57 | /** 58 | * 使用{@link SXSSFWorkbook} 创建WriteExcel对象。使用Streaming Usermodel API去Write出数据到Excel,降低内存占用。 59 | * @param workbook the number of rows that are kept in memory until flushed out, see {@link SXSSFWorkbook}. 60 | * @param tClass 61 | * @return 62 | */ 63 | public static WriteExcelByStreaming excel(XSSFWorkbook workbook, Class tClass) { 64 | return excel(new SXSSFWorkbook(workbook), tClass); 65 | } 66 | 67 | /** 68 | * 使用{@link SXSSFWorkbook} 创建WriteExcel对象。使用Streaming Usermodel API去Write出数据到Excel,降低内存占用。 69 | * @param workbook 70 | * @param rowAccessWindowSize the number of rows that are kept in memory until flushed out, see {@link SXSSFWorkbook}. 71 | * @param tClass 72 | * @return 73 | */ 74 | public static WriteExcelByStreaming excel(XSSFWorkbook workbook, int rowAccessWindowSize, Class tClass) { 75 | return excel(new SXSSFWorkbook(workbook,rowAccessWindowSize), tClass); 76 | } 77 | 78 | /** 79 | * Sheet.clone() is not supported in Streaming userModel. 80 | */ 81 | @Override 82 | public WriteExcel cloneSheet(int sheetIndex){ 83 | throw new IllegalArgumentException("Sheet.clone() is not supported in Streaming userModel."); 84 | } 85 | } -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/usermodel/write/WriteExcelByUserModel.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.usermodel.write; 2 | 3 | import org.apache.poi.ss.usermodel.Workbook; 4 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; 5 | 6 | /** 7 | * Use user model to write excel file.
8 | * userModel —— 9 | *
    10 | *
  • 缺点:会将产生的spreadsheets对象整个保存在内存中,所以write Excel的大小受到堆内存(Heap space)大小限制。
  • 11 | *
  • 优点:使用和理解更简单。
  • 12 | *
  • 使用场景:可以写出数据量较小的Excel。
  • 13 | *
14 | * @author KaiKang 15 | * @since JDK1.8 16 | */ 17 | public class WriteExcelByUserModel extends WriteExcel{ 18 | public WriteExcelByUserModel(Workbook workbook) { 19 | super(workbook); 20 | } 21 | 22 | /** 23 | * 使用{@link Workbook} 创建WriteExcel对象 24 | * @param workbook 25 | * @param tClass 26 | * @return 27 | */ 28 | public static WriteExcelByUserModel excel(Workbook workbook, Class tClass) { 29 | return new WriteExcelByUserModel(workbook); 30 | } 31 | 32 | /** 33 | * 使用{@link XSSFWorkbook} 创建WriteExcel对象。 34 | * @param tClass 35 | * @return 36 | */ 37 | public static WriteExcelByUserModel excel(Class tClass) { 38 | return excel(new XSSFWorkbook(), tClass); 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/util/Assert.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.util; 2 | 3 | import org.apache.commons.collections4.CollectionUtils; 4 | 5 | import java.util.Collection; 6 | 7 | /** 8 | * Assertion utility class that assists in validating arguments. 9 | * @author KaiKang 10 | */ 11 | public class Assert { 12 | 13 | /** 14 | * Assert that a collection contains elements; that is, it must not be 15 | * {@code null} and must contain at least one element. 16 | *
Assert.notEmpty(collection, "Collection must contain elements");
17 | * @param collection the collection to check 18 | * @param message the exception message to use if the assertion fails 19 | * @throws IllegalArgumentException if the collection is {@code null} or 20 | * contains no elements 21 | */ 22 | public static void notEmpty(Collection collection, String message) { 23 | if (CollectionUtils.isEmpty(collection)) { 24 | throw new IllegalArgumentException(message); 25 | } 26 | } 27 | 28 | /** 29 | * Assert that an object is not {@code null}. 30 | *
Assert.notNull(clazz, "The class must not be null");
31 | * @param object the object to check 32 | * @param message the exception message to use if the assertion fails 33 | * @throws IllegalArgumentException if the object is {@code null} 34 | */ 35 | public static void notNull(Object object, String message) { 36 | if (object == null) { 37 | throw new IllegalArgumentException(message); 38 | } 39 | } 40 | 41 | /** 42 | * Assert that an object is {@code null}. 43 | *
Assert.isNull(value, "The value must be null");
44 | * @param object the object to check 45 | * @param message the exception message to use if the assertion fails 46 | * @throws IllegalArgumentException if the object is not {@code null} 47 | */ 48 | public static void isNull(Object object, String message) { 49 | if (object != null) { 50 | throw new IllegalArgumentException(message); 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/liuhuagui/gridexcel/util/ExcelType.java: -------------------------------------------------------------------------------- 1 | package com.github.liuhuagui.gridexcel.util; 2 | 3 | 4 | /** 5 | * Enumeration used to determine whether xls or xlsx should be processed. 6 | * @author KaiKang 7 | */ 8 | public enum ExcelType { 9 | XLS,XLSX; 10 | } 11 | -------------------------------------------------------------------------------- /src/test/java/ReadTest.java: -------------------------------------------------------------------------------- 1 | import bean.Consultant; 2 | import bean.Payment; 3 | import bean.TradeOrder; 4 | import com.alibaba.fastjson.JSON; 5 | import com.github.liuhuagui.gridexcel.GridExcel; 6 | import com.github.liuhuagui.gridexcel.eventmodel.XLSEventModel; 7 | import com.github.liuhuagui.gridexcel.eventmodel.XLSXEventModel; 8 | import com.github.liuhuagui.gridexcel.util.ExcelType; 9 | import org.apache.poi.openxml4j.exceptions.OpenXML4JException; 10 | import org.junit.Test; 11 | import org.xml.sax.SAXException; 12 | 13 | import javax.xml.parsers.ParserConfigurationException; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | /** 21 | * 部分测试样例。
22 | * 其中{@link TradeOrder}、{@link Consultant}、{@link bean.Provider}、{@link bean.Service}、{@link bean.Customer}、{@link Payment} 23 | * 是用来测试的业务对象,目的是展现出GridExcel对关联对象较好的支持能力。使用时可以参照以下样例。 24 | * 25 | * @autor SunKaiKang 26 | * @date 2019/7/26 0026 13:10 27 | */ 28 | public class ReadTest { 29 | /** 30 | * 测试{@link XLSEventModel}。
31 | * 业务逻辑处理方式二选一: 32 | * 1.放在readConsumer函数中。 33 | * 2.使用final or effective final的局部变量存放这写数据,做后续处理。 34 | */ 35 | @Test 36 | public void xlsEventModelTest() throws IOException { 37 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("2003.xls"); 38 | ArrayList arrayList = new ArrayList(); 39 | XLSEventModel xls = new XLSEventModel(resourceAsStream, 1, cs -> { 40 | TradeOrder tradeOrder = new TradeOrder(); 41 | tradeOrder.setTradeOrderId(Long.valueOf(cs.get(0))); 42 | Consultant consultant = new Consultant(); 43 | consultant.setConsultantName(cs.get(3)); 44 | tradeOrder.setConsultant(consultant); 45 | tradeOrder.setPaymentRatio(cs.get(16)); 46 | arrayList.add(tradeOrder); 47 | }); 48 | xls.process(); 49 | System.out.println(JSON.toJSONString(arrayList)); 50 | } 51 | 52 | /** 53 | * 测试{@link XLSXEventModel}。
54 | * 业务逻辑处理方式二选一: 55 | * 1.放在readConsumer函数中。 56 | * 2.使用final or effective final的局部变量存放这写数据,做后续处理。 57 | */ 58 | @Test 59 | public void xlsxEventModelTest() throws IOException, OpenXML4JException, ParserConfigurationException, SAXException { 60 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("2007.xlsx"); 61 | ArrayList arrayList = new ArrayList(); 62 | XLSXEventModel xlsx = new XLSXEventModel(resourceAsStream, 1, cs -> { 63 | TradeOrder tradeOrder = new TradeOrder(); 64 | tradeOrder.setTradeOrderId(Long.valueOf(cs.get(0))); 65 | Consultant consultant = new Consultant(); 66 | consultant.setConsultantName(cs.get(3)); 67 | tradeOrder.setConsultant(consultant); 68 | tradeOrder.setPaymentRatio(cs.get(16)); 69 | arrayList.add(tradeOrder); 70 | }); 71 | xlsx.process(); 72 | System.out.println(JSON.toJSONString(arrayList)); 73 | } 74 | 75 | /** 76 | * 业务逻辑处理方式三选一: 77 | * 1.启用windowListener,并将业务逻辑放在该函数中。 78 | * 2.不启用windowListener,使用get()方法取回全部数据集合,做后续处理。 79 | * 3.readFunction函数,直接放在函数中处理 或 使用final or effective final的局部变量存放这写数据,做后续处理。 80 | * 注意:使用EventModel时readFunction函数的输入为每行的cell值集合List。 81 | * 82 | * @throws Exception 83 | */ 84 | @Test 85 | public void readXlsByEventModel() throws Exception { 86 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("2003.xls"); 87 | GridExcel.readByEventModel(resourceAsStream, TradeOrder.class, ExcelType.XLS) 88 | .window(2, ts -> System.out.println(JSON.toJSONString(ts)))//推荐在这里执行自己的业务逻辑 89 | .process(cs -> { 90 | TradeOrder tradeOrder = new TradeOrder(); 91 | tradeOrder.setTradeOrderId(Long.valueOf(cs.get(0))); 92 | Consultant consultant = new Consultant(); 93 | consultant.setConsultantName(cs.get(3)); 94 | tradeOrder.setConsultant(consultant); 95 | tradeOrder.setPaymentRatio(cs.get(16)); 96 | return tradeOrder; 97 | }, 1); 98 | } 99 | 100 | /** 101 | * 业务逻辑处理方式三选一: 102 | * 1.启用windowListener,并将业务逻辑放在该函数中。 103 | * 2.不启用windowListener,使用get()方法取回全部数据集合,做后续处理。 104 | * 3.readFunction函数,直接放在函数中处理 或 使用final or effective final的局部变量存放这写数据,做后续处理。 105 | * 注意:使用EventModel时readFunction函数的输入为每行的cell值集合List。 106 | * 107 | * @throws Exception 108 | */ 109 | @Test 110 | public void readXlsxByEventModel() throws Exception { 111 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("2007.xlsx"); 112 | GridExcel.readByEventModel(resourceAsStream, TradeOrder.class, ExcelType.XLSX) 113 | .window(2, ts -> System.out.println(JSON.toJSONString(ts)))//推荐在这里执行自己的业务逻辑 114 | .process(cs -> { 115 | TradeOrder tradeOrder = new TradeOrder(); 116 | tradeOrder.setTradeOrderId(Long.valueOf(cs.get(0))); 117 | Consultant consultant = new Consultant(); 118 | consultant.setConsultantName(cs.get(3)); 119 | tradeOrder.setConsultant(consultant); 120 | tradeOrder.setPaymentRatio(cs.get(16)); 121 | return tradeOrder; 122 | }, 1); 123 | } 124 | 125 | /** 126 | * 业务逻辑处理方式三选一: 127 | * 1.启用windowListener,并将业务逻辑放在该函数中。 128 | * 2.不启用windowListener,使用get()方法取回全部数据集合,做后续处理。 129 | * 3.readFunction函数,直接放在函数中处理 或 使用final or effective final的局部变量存放这写数据,做后续处理。 130 | * 注意:使用UserModel时readFunction函数的输入为为Row对象。 131 | * 132 | * @throws Exception 133 | */ 134 | @Test 135 | public void readXlsByUserModel() throws Exception { 136 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("2003.xls"); 137 | GridExcel.readByUserModel(resourceAsStream, TradeOrder.class, ExcelType.XLS) 138 | .window(2, ts -> System.out.println(JSON.toJSONString(ts)))//推荐在这里执行自己的业务逻辑 139 | .process(row -> { 140 | TradeOrder tradeOrder = new TradeOrder(); 141 | tradeOrder.setTradeOrderId(Long.valueOf(row.getCell(0).getStringCellValue())); 142 | Consultant consultant = new Consultant(); 143 | consultant.setConsultantName(row.getCell(3).getStringCellValue()); 144 | tradeOrder.setConsultant(consultant); 145 | tradeOrder.setPaymentRatio(row.getCell(16).getStringCellValue()); 146 | return tradeOrder; 147 | }, 1); 148 | } 149 | 150 | /** 151 | * 业务逻辑处理方式三选一: 152 | * 1.启用windowListener,并将业务逻辑放在该函数中。 153 | * 2.不启用windowListener,使用get()方法取回全部数据集合,做后续处理。 154 | * 3.readFunction函数,直接放在函数中处理 或 使用final or effective final的局部变量存放这写数据,做后续处理。 155 | * 注意:使用UserModel时readFunction函数的输入为为Row对象。 156 | * 157 | * @throws Exception 158 | */ 159 | @Test 160 | public void readXlsxByUserModel() throws Exception { 161 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("2007.xlsx"); 162 | GridExcel.readByUserModel(resourceAsStream, TradeOrder.class, ExcelType.XLSX) 163 | .window(2, ts -> System.out.println(JSON.toJSONString(ts)))//推荐在这里执行自己的业务逻辑 164 | .process(row -> { 165 | TradeOrder tradeOrder = new TradeOrder(); 166 | tradeOrder.setTradeOrderId(Long.valueOf(row.getCell(0).getStringCellValue())); 167 | Consultant consultant = new Consultant(); 168 | consultant.setConsultantName(row.getCell(3).getStringCellValue()); 169 | tradeOrder.setConsultant(consultant); 170 | tradeOrder.setPaymentRatio(row.getCell(16).getStringCellValue()); 171 | return tradeOrder; 172 | }, 1); 173 | } 174 | 175 | /** 176 | * No Entity无实体类读Excel文件 177 | * 业务逻辑处理方式三选一: 178 | * 1.启用windowListener,并将业务逻辑放在该函数中。 179 | * 2.不启用windowListener,使用get()方法取回全部数据集合,做后续处理。 180 | * 3.readFunction函数,直接放在函数中处理 或 使用final or effective final的局部变量存放这写数据,做后续处理。 181 | * 注意:使用EventModel时readFunction函数的输入为每行的cell值集合List。 182 | * 183 | * @throws Exception 184 | */ 185 | @Test 186 | public void readXlsxByEventModelWithoutEntity() throws Exception { 187 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("2007.xlsx"); 188 | GridExcel.readByEventModel(resourceAsStream, Map.class, ExcelType.XLSX) 189 | .window(2, ts -> System.out.println(JSON.toJSONString(ts)))//推荐在这里执行自己的业务逻辑 190 | .process(cs -> { 191 | Map map = new HashMap(); 192 | map.put("tradeOrderId", cs.get(0)); 193 | map.put("consultantName", cs.get(3)); 194 | map.put("paymentRatio", cs.get(16)); 195 | return map; 196 | }, 1); 197 | } 198 | 199 | /** 200 | * 由于空cell造成监听函数——readFunction出现"数组越界"或"索引乱序"问题。
201 | * Issue #3
Issue #4
203 | * Issue #5
204 | * 测试XlsxByEventModel 205 | * 206 | * @throws Exception 207 | */ 208 | @Test 209 | public void readMissingCellXlsxByEventModel() throws Exception { 210 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("test.xlsx"); 211 | GridExcel.readByEventModel(resourceAsStream, Map.class, ExcelType.XLSX) 212 | .window(2, ts -> System.out.println(ts.size()))//推荐在这里执行自己的业务逻辑 213 | .process(cs -> { 214 | System.out.println(cs.get(11)); 215 | return null; 216 | }, 1); 217 | } 218 | 219 | /** 220 | * 由于空cell造成监听函数——readFunction出现"数组越界"或"索引乱序"问题。
221 | * Issue #3
Issue #4
223 | * Issue #5
224 | * 测试XlsByEventModel 225 | * 226 | * @throws Exception 227 | */ 228 | @Test 229 | public void readMissingCellXlsByEventModel() throws Exception { 230 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("test.xls"); 231 | GridExcel.readByEventModel(resourceAsStream, Map.class, ExcelType.XLS) 232 | .window(2, ts -> System.out.println(ts.size()))//推荐在这里执行自己的业务逻辑 233 | .process(cs -> { 234 | // System.out.println(cs.size()); 235 | return null; 236 | }, 1); 237 | } 238 | 239 | 240 | /** 241 | * 由于空cell造成监听函数——readFunction出现"数组越界"或"索引乱序"问题。
242 | * Issue #3
Issue #4
244 | * Issue #5
245 | * 测试XlsxByUserModel 246 | * 247 | * @throws Exception 248 | */ 249 | @Test 250 | public void readMissingCellXlsxByUserModel() throws Exception { 251 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("test.xlsx"); 252 | GridExcel.readByUserModel(resourceAsStream, Map.class, ExcelType.XLSX) 253 | .window(2, ts -> System.out.println(ts.size()))//推荐在这里执行自己的业务逻辑 254 | .process(row -> { 255 | // System.out.println(row.getLastCellNum()); 256 | return null; 257 | }, 1); 258 | } 259 | 260 | /** 261 | * 由于空cell造成监听函数——readFunction出现"数组越界"或"索引乱序"问题。
262 | * Issue #3
Issue #4
264 | * Issue #5
265 | * 测试XlsByUserModel 266 | * 267 | * @throws Exception 268 | */ 269 | @Test 270 | public void readMissingCellXlsByUserModel() throws Exception { 271 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("test.xls"); 272 | GridExcel.readByUserModel(resourceAsStream, Map.class, ExcelType.XLS) 273 | .window(2, ts -> System.out.println(ts.size()))//推荐在这里执行自己的业务逻辑 274 | .process(row -> { 275 | // System.out.println(row.getLastCellNum()); 276 | return null; 277 | }, 1); 278 | } 279 | 280 | } 281 | -------------------------------------------------------------------------------- /src/test/java/WriteTest.java: -------------------------------------------------------------------------------- 1 | import bean.Payment; 2 | import bean.TradeOrder; 3 | import com.github.liuhuagui.gridexcel.GridExcel; 4 | import org.apache.commons.io.FileUtils; 5 | import org.junit.Test; 6 | import util.MockData; 7 | import util.PaymentWays; 8 | 9 | import java.io.File; 10 | import java.util.LinkedHashMap; 11 | import java.util.function.Function; 12 | 13 | /** 14 | * 使用MockData从类路径下的序列化对象文件获取模拟数据List 15 | * @author KaiKang 16 | */ 17 | public class WriteTest { 18 | 19 | private void putPaymentsFunction(LinkedHashMap> writeFunctionMap, final int index) { 20 | writeFunctionMap.put("第" + (1 + index) + "次支付订单号", to -> { 21 | Payment payment = to.getPayments().get(index); 22 | return payment.getPaymentId(); 23 | }); 24 | writeFunctionMap.put("第" + (1 + index) + "次支付金额", to -> { 25 | Payment payment = to.getPayments().get(index); 26 | return payment.getPaymentAmount(); 27 | }); 28 | writeFunctionMap.put("第" + (1 + index) + "次支付方式", to -> { 29 | Payment payment = to.getPayments().get(index); 30 | return PaymentWays.bankInstName(payment.getPaymentWay()); 31 | }); 32 | writeFunctionMap.put("第" + (1 + index) + "次支付状态", to -> { 33 | Payment payment = to.getPayments().get(index); 34 | return payment.getPaymentStatus() == 1 ? "支付完成" : "支付中"; 35 | }); 36 | 37 | } 38 | 39 | public LinkedHashMap> writeFunctionMap(){ 40 | LinkedHashMap> writeFunctionMap = new LinkedHashMap<>(); 41 | writeFunctionMap.put("订单号", to -> to.getTradeOrderId()); 42 | writeFunctionMap.put("顾问编号", to -> to.getConsultantId()); 43 | writeFunctionMap.put("顾问姓名", to -> to.getConsultant().getConsultantName()); 44 | writeFunctionMap.put("顾问手机号", to -> to.getConsultant().getConsultantPhone()); 45 | writeFunctionMap.put("客户编号", to -> to.getCustomerId()); 46 | writeFunctionMap.put("客户名称", to -> to.getCustomer().getCustomerName()); 47 | writeFunctionMap.put("客户手机号", to -> to.getCustomer().getCustomerPhone()); 48 | writeFunctionMap.put("服务商编号", to -> to.getProviderId()); 49 | writeFunctionMap.put("服务商名称", to -> to.getProvider().getProviderName()); 50 | writeFunctionMap.put("服务商注册手机号", to -> to.getProvider().getLegalPersonPhone()); 51 | writeFunctionMap.put("服务编号", to -> to.getServiceId()); 52 | writeFunctionMap.put("服务名称", to -> to.getService().getServiceName()); 53 | writeFunctionMap.put("单价", to -> to.getActualUnitPrice()); 54 | writeFunctionMap.put("数量", to -> to.getServiceCount()); 55 | writeFunctionMap.put("总额", to -> to.getActualSumPrice()); 56 | writeFunctionMap.put("支付比例", to -> to.getPaymentRatio()); 57 | writeFunctionMap.put("支付次数", to -> to.getPaymentTimes()); 58 | writeFunctionMap.put("支付状态", to -> { 59 | Byte status = to.getPaymentStatus(); 60 | return status == null ? "未支付" : ("第" + Math.abs(status) + "次支付" + (status > 0 ? "完成" : "中")); 61 | }); 62 | // 为支付订单添加处理函数 63 | putPaymentsFunction(writeFunctionMap, 0); 64 | putPaymentsFunction(writeFunctionMap, 1); 65 | putPaymentsFunction(writeFunctionMap, 2); 66 | 67 | return writeFunctionMap; 68 | } 69 | 70 | 71 | /** 72 | * 使用UserModel写出数据到Excel 73 | * @throws Exception 74 | */ 75 | @Test 76 | public void writeExcelByUserModel() throws Exception { 77 | GridExcel.writeByUserModel(TradeOrder.class) 78 | .head(writeFunctionMap())//对象字段到Excel列的映射 79 | .createSheet() 80 | .process(MockData.data())//模拟数据。在这里设置业务数据集合。 81 | .write(FileUtils.openOutputStream(new File("/excel/test.xlsx"))); 82 | } 83 | 84 | /** 85 | * 使用Streaming UserModel写出数据到Excel 86 | * @throws Exception 87 | */ 88 | @Test 89 | public void writeExcelByStreaming() throws Exception { 90 | GridExcel.writeByStreaming(TradeOrder.class) 91 | .head(writeFunctionMap())//对象字段到Excel列的映射 92 | .createSheet() 93 | .process(MockData.data())//模拟数据。在这里设置业务数据集合。 94 | .write(FileUtils.openOutputStream(new File("/excel/test.xlsx"))); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/bean/Consultant.java: -------------------------------------------------------------------------------- 1 | package bean; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 7 | * databaseTable:consultant 8 | */ 9 | public class Consultant implements Serializable{ 10 | 11 | /** 12 | * 顾问ID 13 | */ 14 | private String consultantId; 15 | 16 | /** 17 | * 服务商ID 18 | */ 19 | private String providerId; 20 | 21 | /** 22 | * 顾问姓名 23 | */ 24 | private String consultantName; 25 | 26 | /** 27 | * 顾问性别 28 | */ 29 | private String consultantGender; 30 | 31 | /** 32 | * 头像路径 33 | */ 34 | private String consultantImage; 35 | 36 | /** 37 | * 身份证号 38 | */ 39 | private String consultantIdentity; 40 | 41 | /** 42 | * 入职日期 43 | */ 44 | private String joinedDate; 45 | 46 | /** 47 | * 综合描述 48 | */ 49 | private String consultantDescribe; 50 | 51 | /** 52 | * 手机号 53 | */ 54 | private String consultantPhone; 55 | 56 | 57 | /** 58 | * 联系方式 59 | */ 60 | private String contactPhone; 61 | 62 | /** 63 | * 主管名称 64 | */ 65 | private String masterName; 66 | 67 | /** 68 | * 主管手机号 69 | */ 70 | private String masterPhone; 71 | 72 | /** 73 | * 验证码 74 | */ 75 | private String validateNum; 76 | 77 | /** 78 | * 验证码过期时间 79 | */ 80 | private String validateTime; 81 | 82 | /** 83 | * 顾问类型 84 | */ 85 | private String consultantType; 86 | 87 | /** 88 | * 顾问密码 89 | */ 90 | private String passWord; 91 | 92 | /** 93 | * 服务区域 94 | */ 95 | private String serviceArea; 96 | 97 | /** 98 | * 服务范围,西文“,"分离 99 | */ 100 | private String serviceContent; 101 | 102 | /** 103 | * 服务数量 104 | */ 105 | private Integer serviceCount; 106 | 107 | /** 108 | * 好评率 109 | */ 110 | private String favorableRate; 111 | 112 | /** 113 | * 关注顾问的客户总数 114 | */ 115 | private Integer byCustomerCount; 116 | 117 | public String findKey(){ 118 | return consultantId; 119 | } 120 | 121 | public String getMasterName() { 122 | return masterName; 123 | } 124 | 125 | 126 | public void setMasterName(String masterName) { 127 | this.masterName = masterName; 128 | } 129 | 130 | public String getMasterPhone() { 131 | return masterPhone; 132 | } 133 | 134 | public void setMasterPhone(String masterPhone) { 135 | this.masterPhone = masterPhone; 136 | } 137 | 138 | public Integer getByCustomerCount() { 139 | return byCustomerCount; 140 | } 141 | 142 | 143 | public void setByCustomerCount(Integer byCustomerCount) { 144 | this.byCustomerCount = byCustomerCount; 145 | } 146 | 147 | 148 | public String getContactPhone() { 149 | return contactPhone; 150 | } 151 | 152 | public void setContactPhone(String contactPhone) { 153 | this.contactPhone = contactPhone; 154 | } 155 | 156 | 157 | public String getValidateNum() { 158 | return validateNum; 159 | } 160 | 161 | public void setValidateNum(String validateNum) { 162 | this.validateNum = validateNum; 163 | } 164 | 165 | public String getValidateTime() { 166 | return validateTime; 167 | } 168 | 169 | 170 | public void setValidateTime(String validateTime) { 171 | this.validateTime = validateTime; 172 | } 173 | 174 | 175 | 176 | public String getServiceContent() { 177 | return serviceContent; 178 | } 179 | 180 | 181 | public void setServiceContent(String serviceContent) { 182 | this.serviceContent = serviceContent; 183 | } 184 | 185 | 186 | public String getConsultantId() { 187 | return consultantId; 188 | } 189 | 190 | public void setConsultantId(String consultantId) { 191 | this.consultantId = consultantId; 192 | } 193 | 194 | public String getProviderId() { 195 | return providerId; 196 | } 197 | 198 | public void setProviderId(String providerId) { 199 | this.providerId = providerId; 200 | } 201 | 202 | public String getConsultantName() { 203 | return consultantName; 204 | } 205 | 206 | public void setConsultantName(String consultantName) { 207 | this.consultantName = consultantName; 208 | } 209 | 210 | public String getConsultantGender() { 211 | return consultantGender; 212 | } 213 | 214 | public void setConsultantGender(String consultantGender) { 215 | this.consultantGender = consultantGender; 216 | } 217 | 218 | public String getConsultantImage() { 219 | return consultantImage; 220 | } 221 | 222 | public void setConsultantImage(String consultantImage) { 223 | this.consultantImage = consultantImage; 224 | } 225 | 226 | public String getConsultantIdentity() { 227 | return consultantIdentity; 228 | } 229 | 230 | public void setConsultantIdentity(String consultantIdentity) { 231 | this.consultantIdentity = consultantIdentity; 232 | } 233 | 234 | public String getJoinedDate() { 235 | return joinedDate; 236 | } 237 | 238 | public void setJoinedDate(String joinedDate) { 239 | this.joinedDate = joinedDate; 240 | } 241 | 242 | public String getConsultantDescribe() { 243 | return consultantDescribe; 244 | } 245 | 246 | public void setConsultantDescribe(String consultantDescribe) { 247 | this.consultantDescribe = consultantDescribe; 248 | } 249 | 250 | public String getConsultantPhone() { 251 | return consultantPhone; 252 | } 253 | 254 | public void setConsultantPhone(String consultantPhone) { 255 | this.consultantPhone = consultantPhone; 256 | } 257 | 258 | public String getConsultantType() { 259 | return consultantType; 260 | } 261 | 262 | public void setConsultantType(String consultantType) { 263 | this.consultantType = consultantType; 264 | } 265 | 266 | public String getPassWord() { 267 | return passWord; 268 | } 269 | 270 | public void setPassWord(String passWord) { 271 | this.passWord = passWord; 272 | } 273 | 274 | public String getServiceArea() { 275 | return serviceArea; 276 | } 277 | 278 | public void setServiceArea(String serviceArea) { 279 | this.serviceArea = serviceArea; 280 | } 281 | 282 | public Integer getServiceCount() { 283 | return serviceCount; 284 | } 285 | 286 | public void setServiceCount(Integer serviceCount) { 287 | this.serviceCount = serviceCount; 288 | } 289 | 290 | public String getFavorableRate() { 291 | return favorableRate; 292 | } 293 | 294 | public void setFavorableRate(String favorableRate) { 295 | this.favorableRate = favorableRate; 296 | } 297 | } -------------------------------------------------------------------------------- /src/test/java/bean/Customer.java: -------------------------------------------------------------------------------- 1 | package bean; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 7 | * databaseTable:customer 8 | */ 9 | public class Customer implements Serializable { 10 | /** 11 | * 客户ID 12 | */ 13 | private String customerId; 14 | 15 | /** 16 | * 客户联系方式 17 | */ 18 | private String customerPhone; 19 | 20 | /** 21 | * 密码 22 | */ 23 | private String passWord; 24 | 25 | /** 26 | * 新密码 27 | */ 28 | private String newPassWord; 29 | 30 | /** 31 | * 验证码 32 | */ 33 | private String validateNum; 34 | 35 | /** 36 | * 失效时间 37 | */ 38 | private String validateTime; 39 | 40 | /** 41 | * 冻结时间 42 | */ 43 | private String frozenTime; 44 | 45 | /** 46 | * 解冻时间 47 | */ 48 | private String thawingTime; 49 | 50 | /** 51 | * 昵称 52 | */ 53 | private String customerName; 54 | 55 | /** 56 | * 头像路径 57 | */ 58 | private String customerImage; 59 | 60 | /** 61 | * 客户类型 62 | */ 63 | private String customerType; 64 | 65 | /** 66 | * 邮箱 67 | */ 68 | private String customerEmail; 69 | 70 | /** 71 | * 职业 72 | */ 73 | private String customerProfession; 74 | 75 | /** 76 | * 城市 77 | */ 78 | private String customerCity; 79 | 80 | /** 81 | * 了解渠道 82 | */ 83 | private String knowWay; 84 | 85 | /** 86 | * 未读通知数 87 | */ 88 | private Integer unreadNotificationCount; 89 | 90 | /** 91 | * 已读通知数 92 | */ 93 | private Integer readNotificationCount; 94 | 95 | /** 96 | * 关注的顾问总数 97 | */ 98 | private Integer toConsultantCount; 99 | 100 | public String findKey() { 101 | return customerId; 102 | } 103 | 104 | public Integer getToConsultantCount() { 105 | return toConsultantCount; 106 | } 107 | 108 | public void setToConsultantCount(Integer toConsultantCount) { 109 | this.toConsultantCount = toConsultantCount; 110 | } 111 | 112 | public Integer getUnreadNotificationCount() { 113 | return unreadNotificationCount; 114 | } 115 | 116 | public void setUnreadNotificationCount(Integer unreadNotificationCount) { 117 | this.unreadNotificationCount = unreadNotificationCount; 118 | } 119 | 120 | public Integer getReadNotificationCount() { 121 | return readNotificationCount; 122 | } 123 | 124 | public void setReadNotificationCount(Integer readNotificationCount) { 125 | this.readNotificationCount = readNotificationCount; 126 | } 127 | 128 | public String getFrozenTime() { 129 | return frozenTime; 130 | } 131 | 132 | public void setFrozenTime(String frozenTime) { 133 | this.frozenTime = frozenTime; 134 | } 135 | 136 | public String getThawingTime() { 137 | return thawingTime; 138 | } 139 | 140 | public void setThawingTime(String thawingTime) { 141 | this.thawingTime = thawingTime; 142 | } 143 | 144 | public String getCustomerId() { 145 | return customerId; 146 | } 147 | 148 | public void setCustomerId(String customerId) { 149 | this.customerId = customerId == null ? null : (customerId.trim().length()==0?null:customerId); 150 | } 151 | 152 | public String getNewPassWord() { 153 | return newPassWord; 154 | } 155 | 156 | public void setNewPassWord(String newPassWord) { 157 | this.newPassWord = newPassWord; 158 | } 159 | 160 | public String getCustomerPhone() { 161 | return customerPhone; 162 | } 163 | 164 | public void setCustomerPhone(String customerPhone) { 165 | this.customerPhone = customerPhone == null ? null : (customerPhone.trim().length()==0?null:customerPhone); 166 | } 167 | 168 | public String getPassWord() { 169 | return passWord; 170 | } 171 | 172 | public void setPassWord(String passWord) { 173 | this.passWord = passWord == null ? null : (passWord.trim().length()==0?null:passWord); 174 | } 175 | 176 | public String getValidateNum() { 177 | return validateNum; 178 | } 179 | 180 | public void setValidateNum(String validateNum) { 181 | this.validateNum = validateNum == null ? null : (validateNum.trim().length()==0?null:validateNum); 182 | } 183 | 184 | public String getValidateTime() { 185 | return validateTime; 186 | } 187 | 188 | public void setValidateTime(String validateTime) { 189 | this.validateTime = validateTime == null ? null : (validateTime.trim().length()==0?null:validateTime); 190 | } 191 | 192 | public String getCustomerName() { 193 | return customerName; 194 | } 195 | 196 | public void setCustomerName(String customerName) { 197 | this.customerName = customerName == null ? null : (customerName.trim().length()==0?null:customerName); 198 | } 199 | 200 | public String getCustomerImage() { 201 | return customerImage; 202 | } 203 | 204 | public void setCustomerImage(String customerImage) { 205 | this.customerImage = customerImage == null ? null : (customerImage.trim().length()==0?null:customerImage); 206 | } 207 | 208 | public String getCustomerType() { 209 | return customerType; 210 | } 211 | 212 | public void setCustomerType(String customerType) { 213 | this.customerType = customerType == null ? null : (customerType.trim().length()==0?null:customerType); 214 | } 215 | 216 | public String getCustomerEmail() { 217 | return customerEmail; 218 | } 219 | 220 | public void setCustomerEmail(String customerEmail) { 221 | this.customerEmail = customerEmail == null ? null : (customerEmail.trim().length()==0?null:customerEmail); 222 | } 223 | 224 | public String getCustomerProfession() { 225 | return customerProfession; 226 | } 227 | 228 | public void setCustomerProfession(String customerProfession) { 229 | this.customerProfession = customerProfession == null ? null : (customerProfession.trim().length()==0?null:customerProfession); 230 | } 231 | 232 | public String getCustomerCity() { 233 | return customerCity; 234 | } 235 | 236 | public void setCustomerCity(String customerCity) { 237 | this.customerCity = customerCity == null ? null : (customerCity.trim().length()==0?null:customerCity); 238 | } 239 | 240 | public String getKnowWay() { 241 | return knowWay; 242 | } 243 | 244 | public void setKnowWay(String knowWay) { 245 | this.knowWay = knowWay == null ? null : (knowWay.trim().length()==0?null:knowWay); 246 | } 247 | } -------------------------------------------------------------------------------- /src/test/java/bean/Payment.java: -------------------------------------------------------------------------------- 1 | package bean; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 7 | * databaseTable:payment 8 | */ 9 | public class Payment implements Serializable{ 10 | /** 11 | * 支付id 12 | */ 13 | private Long paymentId; 14 | 15 | /** 16 | * 交易订单号 17 | */ 18 | private Long tradeOrderId; 19 | 20 | /** 21 | * 支付阶段 22 | */ 23 | private Byte paymentPhase; 24 | 25 | /** 26 | * 支付权重,<100 27 | */ 28 | private Byte paymentWeight; 29 | 30 | /** 31 | * 支付金额 32 | */ 33 | private Double paymentAmount; 34 | 35 | /** 36 | * 支付方式,0微信,1支付宝,其他——银联 37 | */ 38 | private String paymentWay; 39 | 40 | /** 41 | * 支付状态, 0未支付 1支付成功 42 | */ 43 | private Byte paymentStatus; 44 | 45 | public Payment() { 46 | } 47 | 48 | public Payment(Long tradeOrderId) { 49 | super(); 50 | this.tradeOrderId = tradeOrderId; 51 | } 52 | 53 | public Long getPaymentId() { 54 | return paymentId; 55 | } 56 | 57 | public void setPaymentId(Long paymentId) { 58 | this.paymentId = paymentId; 59 | } 60 | 61 | public Long getTradeOrderId() { 62 | return tradeOrderId; 63 | } 64 | 65 | public void setTradeOrderId(Long tradeOrderId) { 66 | this.tradeOrderId = tradeOrderId; 67 | } 68 | 69 | public Byte getPaymentPhase() { 70 | return paymentPhase; 71 | } 72 | 73 | public void setPaymentPhase(Byte paymentPhase) { 74 | this.paymentPhase = paymentPhase; 75 | } 76 | 77 | public Byte getPaymentWeight() { 78 | return paymentWeight; 79 | } 80 | 81 | public void setPaymentWeight(Byte paymentWeight) { 82 | this.paymentWeight = paymentWeight; 83 | } 84 | 85 | public Double getPaymentAmount() { 86 | return paymentAmount; 87 | } 88 | 89 | public void setPaymentAmount(Double paymentAmount) { 90 | this.paymentAmount = paymentAmount; 91 | } 92 | 93 | public String getPaymentWay() { 94 | return paymentWay; 95 | } 96 | 97 | public void setPaymentWay(String paymentWay) { 98 | this.paymentWay = paymentWay; 99 | } 100 | 101 | public Byte getPaymentStatus() { 102 | return paymentStatus; 103 | } 104 | 105 | public void setPaymentStatus(Byte paymentStatus) { 106 | this.paymentStatus = paymentStatus; 107 | } 108 | } -------------------------------------------------------------------------------- /src/test/java/bean/Provider.java: -------------------------------------------------------------------------------- 1 | package bean; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | 7 | 8 | /** 9 | * 10 | * databaseTable:provider 11 | */ 12 | public class Provider implements Serializable { 13 | /** 14 | * 服务商ID 15 | */ 16 | private String providerId; 17 | 18 | /** 19 | * 服务商名称 20 | */ 21 | private String providerName; 22 | 23 | /** 24 | * 服务商性质 25 | */ 26 | private String providerCharacter; 27 | 28 | /** 29 | * 头像路径 30 | */ 31 | private String providerImage; 32 | 33 | /** 34 | * 描述 35 | */ 36 | private String providerDescribe; 37 | 38 | /** 39 | * 企业地址 40 | */ 41 | private String providerAddress; 42 | 43 | /** 44 | * 企业邮箱 45 | */ 46 | private String providerEmail; 47 | 48 | /** 49 | * 法人姓名 50 | */ 51 | private String legalPersonName; 52 | 53 | /** 54 | * 法人性别 55 | */ 56 | private String legalPersonGender; 57 | 58 | /** 59 | * 法人身份证号 60 | */ 61 | private String legalPersonIdentityNum; 62 | 63 | /** 64 | * 身份证正面照路径 65 | */ 66 | private String legalPersonIdentityFore; 67 | 68 | /** 69 | * 身份证背面照路径 70 | */ 71 | private String legalPersonIdentityBack; 72 | 73 | /** 74 | * 法人手机号 75 | */ 76 | private String legalPersonPhone; 77 | 78 | /** 79 | * 密码 80 | */ 81 | private String passWord; 82 | 83 | /** 84 | * 新密码 85 | */ 86 | private String newPassWord; 87 | 88 | /** 89 | * 验证码 90 | */ 91 | private String validateNum; 92 | 93 | /** 94 | * 过期时间 95 | */ 96 | private String validateTime; 97 | 98 | /** 99 | * 冻结时间 100 | */ 101 | private String frozenTime; 102 | 103 | /** 104 | * 解冻时间 105 | */ 106 | private String thawingTime; 107 | 108 | /** 109 | * 经营许可证编号 110 | */ 111 | private String businessLicenceNum; 112 | 113 | /** 114 | * 经营许可证路径 115 | */ 116 | private String businessLicenceUrl; 117 | 118 | /** 119 | * 服务商下顾问的初始密码 120 | */ 121 | private String defaultPassWord; 122 | 123 | /** 124 | * 服务商状态 125 | */ 126 | private String providerType; 127 | 128 | /** 129 | * 未读通知数 130 | */ 131 | private Integer unreadNotificationCount; 132 | 133 | /** 134 | * 已读通知数 135 | */ 136 | private Integer readNotificationCount; 137 | 138 | /** 139 | * 订单总数 140 | */ 141 | private Integer orderQuantity; 142 | 143 | /** 144 | * 待结算订单总数 145 | */ 146 | private Integer pendingSettlementQuantity; 147 | 148 | /** 149 | * 服务上下顾问总数 150 | */ 151 | private Integer consultantQuantity; 152 | 153 | /** 154 | * 服务项目总数 155 | */ 156 | private Integer serviceQuantity; 157 | 158 | 159 | public Integer getUnreadNotificationCount() { 160 | return unreadNotificationCount; 161 | } 162 | 163 | public void setUnreadNotificationCount(Integer unreadNotificationCount) { 164 | this.unreadNotificationCount = unreadNotificationCount; 165 | } 166 | 167 | public Integer getReadNotificationCount() { 168 | return readNotificationCount; 169 | } 170 | 171 | public void setReadNotificationCount(Integer readNotificationCount) { 172 | this.readNotificationCount = readNotificationCount; 173 | } 174 | 175 | public Integer getOrderQuantity() { 176 | return orderQuantity; 177 | } 178 | 179 | public void setOrderQuantity(Integer orderQuantity) { 180 | this.orderQuantity = orderQuantity; 181 | } 182 | 183 | public Integer getPendingSettlementQuantity() { 184 | return pendingSettlementQuantity; 185 | } 186 | 187 | public void setPendingSettlementQuantity(Integer pendingSettlementQuantity) { 188 | this.pendingSettlementQuantity = pendingSettlementQuantity; 189 | } 190 | 191 | public Integer getConsultantQuantity() { 192 | return consultantQuantity; 193 | } 194 | 195 | public void setConsultantQuantity(Integer consultantQuantity) { 196 | this.consultantQuantity = consultantQuantity; 197 | } 198 | 199 | public Integer getServiceQuantity() { 200 | return serviceQuantity; 201 | } 202 | 203 | public void setServiceQuantity(Integer serviceQuantity) { 204 | this.serviceQuantity = serviceQuantity; 205 | } 206 | 207 | public String getProviderCharacter() { 208 | return providerCharacter; 209 | } 210 | 211 | public void setProviderCharacter(String providerCharacter) { 212 | this.providerCharacter = providerCharacter; 213 | } 214 | 215 | public String getFrozenTime() { 216 | return frozenTime; 217 | } 218 | 219 | public void setFrozenTime(String frozenTime) { 220 | this.frozenTime = frozenTime; 221 | } 222 | 223 | public String getThawingTime() { 224 | return thawingTime; 225 | } 226 | 227 | 228 | public void setThawingTime(String thawingTime) { 229 | this.thawingTime = thawingTime; 230 | } 231 | 232 | 233 | public String getProviderId() { 234 | return providerId; 235 | } 236 | 237 | public void setProviderId(String providerId) { 238 | this.providerId = providerId == null ? null : (providerId.trim().length()==0?null:providerId); 239 | } 240 | 241 | public String getNewPassWord() { 242 | return newPassWord; 243 | } 244 | 245 | public void setNewPassWord(String newPassWord) { 246 | this.newPassWord = newPassWord; 247 | } 248 | 249 | public String getProviderType() { 250 | return providerType; 251 | } 252 | 253 | public void setProviderType(String providerType) { 254 | this.providerType = providerType; 255 | } 256 | 257 | public String getDefaultPassWord() { 258 | return defaultPassWord; 259 | } 260 | 261 | public void setDefaultPassWord(String defaultPassWord) { 262 | this.defaultPassWord = defaultPassWord; 263 | } 264 | 265 | public String getProviderName() { 266 | return providerName; 267 | } 268 | 269 | public void setProviderName(String providerName) { 270 | this.providerName = providerName == null ? null : (providerName.trim().length()==0?null:providerName); 271 | } 272 | 273 | public String getProviderImage() { 274 | return providerImage; 275 | } 276 | 277 | public void setProviderImage(String providerImage) { 278 | this.providerImage = providerImage == null ? null : (providerImage.trim().length()==0?null:providerImage); 279 | } 280 | 281 | public String getProviderDescribe() { 282 | return providerDescribe; 283 | } 284 | 285 | public void setProviderDescribe(String providerDescribe) { 286 | this.providerDescribe = providerDescribe == null ? null : (providerDescribe.trim().length()==0?null:providerDescribe); 287 | } 288 | 289 | public String getProviderAddress() { 290 | return providerAddress; 291 | } 292 | 293 | public void setProviderAddress(String providerAddress) { 294 | this.providerAddress = providerAddress == null ? null : (providerAddress.trim().length()==0?null:providerAddress); 295 | } 296 | 297 | public String getProviderEmail() { 298 | return providerEmail; 299 | } 300 | 301 | public void setProviderEmail(String providerEmail) { 302 | this.providerEmail = providerEmail == null ? null : (providerEmail.trim().length()==0?null:providerEmail); 303 | } 304 | 305 | public String getLegalPersonName() { 306 | return legalPersonName; 307 | } 308 | 309 | public void setLegalPersonName(String legalPersonName) { 310 | this.legalPersonName = legalPersonName == null ? null : (legalPersonName.trim().length()==0?null:legalPersonName); 311 | } 312 | 313 | public String getLegalPersonGender() { 314 | return legalPersonGender; 315 | } 316 | 317 | public void setLegalPersonGender(String legalPersonGender) { 318 | this.legalPersonGender = legalPersonGender == null ? null : (legalPersonGender.trim().length()==0?null:legalPersonGender); 319 | } 320 | 321 | public String getLegalPersonIdentityNum() { 322 | return legalPersonIdentityNum; 323 | } 324 | 325 | public void setLegalPersonIdentityNum(String legalPersonIdentityNum) { 326 | this.legalPersonIdentityNum = legalPersonIdentityNum == null ? null : (legalPersonIdentityNum.trim().length()==0?null:legalPersonIdentityNum); 327 | } 328 | 329 | public String getLegalPersonIdentityFore() { 330 | return legalPersonIdentityFore; 331 | } 332 | 333 | public void setLegalPersonIdentityFore(String legalPersonIdentityFore) { 334 | this.legalPersonIdentityFore = legalPersonIdentityFore == null ? null : (legalPersonIdentityFore.trim().length()==0?null:legalPersonIdentityFore); 335 | } 336 | 337 | public String getLegalPersonIdentityBack() { 338 | return legalPersonIdentityBack; 339 | } 340 | 341 | public void setLegalPersonIdentityBack(String legalPersonIdentityBack) { 342 | this.legalPersonIdentityBack = legalPersonIdentityBack == null ? null : (legalPersonIdentityBack.trim().length()==0?null:legalPersonIdentityBack); 343 | } 344 | 345 | public String getLegalPersonPhone() { 346 | return legalPersonPhone; 347 | } 348 | 349 | public void setLegalPersonPhone(String legalPersonPhone) { 350 | this.legalPersonPhone = legalPersonPhone == null ? null : (legalPersonPhone.trim().length()==0?null:legalPersonPhone); 351 | } 352 | 353 | public String getPassWord() { 354 | return passWord; 355 | } 356 | 357 | public void setPassWord(String passWord) { 358 | this.passWord = passWord == null ? null : (passWord.trim().length()==0?null:passWord); 359 | } 360 | 361 | public String getValidateNum() { 362 | return validateNum; 363 | } 364 | 365 | public void setValidateNum(String validateNum) { 366 | this.validateNum = validateNum == null ? null : (validateNum.trim().length()==0?null:validateNum); 367 | } 368 | 369 | public String getValidateTime() { 370 | return validateTime; 371 | } 372 | 373 | public void setValidateTime(String validateTime) { 374 | this.validateTime = validateTime == null ? null : (validateTime.trim().length()==0?null:validateTime); 375 | } 376 | 377 | public String getBusinessLicenceNum() { 378 | return businessLicenceNum; 379 | } 380 | 381 | public void setBusinessLicenceNum(String businessLicenceNum) { 382 | this.businessLicenceNum = businessLicenceNum == null ? null : (businessLicenceNum.trim().length()==0?null:businessLicenceNum); 383 | } 384 | 385 | public String getBusinessLicenceUrl() { 386 | return businessLicenceUrl; 387 | } 388 | 389 | public void setBusinessLicenceUrl(String businessLicenceUrl) { 390 | this.businessLicenceUrl = businessLicenceUrl == null ? null : (businessLicenceUrl.trim().length()==0?null:businessLicenceUrl); 391 | } 392 | } -------------------------------------------------------------------------------- /src/test/java/bean/Service.java: -------------------------------------------------------------------------------- 1 | package bean; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 7 | * databaseTable:service 8 | */ 9 | public class Service implements Serializable{ 10 | 11 | /** 12 | * 服务ID 13 | */ 14 | private String serviceId; 15 | 16 | /** 17 | * 服务名称 18 | */ 19 | private String serviceName; 20 | 21 | /** 22 | * 服务图片路径 23 | */ 24 | private String serviceImage; 25 | 26 | /** 27 | * 二级服务ID 28 | */ 29 | private String secondServiceId; 30 | 31 | /** 32 | * 服务描述 33 | */ 34 | private String serviceDescribe; 35 | 36 | /** 37 | * 单位价格 38 | */ 39 | private String unitPrice; 40 | 41 | public String getServiceId() { 42 | return serviceId; 43 | } 44 | 45 | public void setServiceId(String serviceId) { 46 | this.serviceId = serviceId == null ? null : (serviceId.trim().length()==0?null:serviceId); 47 | } 48 | 49 | public String getServiceName() { 50 | return serviceName; 51 | } 52 | 53 | public void setServiceName(String serviceName) { 54 | this.serviceName = serviceName == null ? null : (serviceName.trim().length()==0?null:serviceName); 55 | } 56 | 57 | public String getServiceImage() { 58 | return serviceImage; 59 | } 60 | 61 | public void setServiceImage(String serviceImage) { 62 | this.serviceImage = serviceImage == null ? null : (serviceImage.trim().length()==0?null:serviceImage); 63 | } 64 | 65 | public String getSecondServiceId() { 66 | return secondServiceId; 67 | } 68 | 69 | public void setSecondServiceId(String secondServiceId) { 70 | this.secondServiceId = secondServiceId; 71 | } 72 | 73 | public String getServiceDescribe() { 74 | return serviceDescribe; 75 | } 76 | 77 | public void setServiceDescribe(String serviceDescribe) { 78 | this.serviceDescribe = serviceDescribe == null ? null : (serviceDescribe.trim().length()==0?null:serviceDescribe); 79 | } 80 | 81 | public String getUnitPrice() { 82 | return unitPrice; 83 | } 84 | 85 | public void setUnitPrice(String unitPrice) { 86 | this.unitPrice = unitPrice; 87 | } 88 | 89 | 90 | } -------------------------------------------------------------------------------- /src/test/java/bean/TradeOrder.java: -------------------------------------------------------------------------------- 1 | package bean; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | /** 7 | * 8 | * databaseTable:trade_order 9 | */ 10 | public class TradeOrder implements Serializable{ 11 | 12 | /** 13 | * 交易订单号 14 | */ 15 | private Long tradeOrderId; 16 | 17 | /** 18 | * 交易类型 19 | */ 20 | private Byte tradeType; 21 | 22 | /** 23 | * 支付比例 24 | */ 25 | private String paymentRatio; 26 | 27 | /** 28 | * 支付次数 29 | */ 30 | private Byte paymentTimes; 31 | 32 | /** 33 | * 服务ID 34 | */ 35 | private Long serviceId; 36 | 37 | /** 38 | * 服务商Id 39 | */ 40 | private String providerId; 41 | 42 | /** 43 | * 客户ID 44 | */ 45 | private String customerId; 46 | 47 | /** 48 | * 顾问ID 49 | */ 50 | private String consultantId; 51 | 52 | /** 53 | * 标准单位价格 54 | */ 55 | private Double standardUnitPrice; 56 | 57 | /** 58 | * 实际单位价格 59 | */ 60 | private Double actualUnitPrice; 61 | 62 | /** 63 | * 服务数量 64 | */ 65 | private Integer serviceCount; 66 | 67 | /** 68 | * 标准总价 69 | */ 70 | private Double standardSumPrice; 71 | 72 | /** 73 | * 实际总价 74 | */ 75 | private Double actualSumPrice; 76 | 77 | /** 78 | * 提前提醒时间 79 | */ 80 | private String advancedRemindTime; 81 | 82 | /** 83 | * 强制更新流程时间 84 | */ 85 | private String mandatoryUpdateTime; 86 | 87 | /** 88 | * 当前流程序号 89 | */ 90 | private Integer currentProcessNum; 91 | 92 | 93 | /** 94 | * 交易地址 95 | */ 96 | private String tradeAddress; 97 | 98 | /** 99 | * n(|n|∈N+) 第|n|次支付,<0支付中,>0支付完成 100 | */ 101 | private Byte paymentStatus; 102 | /** 103 | * 关联集合的属性 104 | */ 105 | private List payments; 106 | 107 | private Customer customer; 108 | 109 | private Provider provider; 110 | 111 | private Consultant consultant; 112 | 113 | private Service service; 114 | 115 | public Byte getPaymentStatus() { 116 | return paymentStatus; 117 | } 118 | 119 | public void setPaymentStatus(Byte paymentStatus) { 120 | this.paymentStatus = paymentStatus; 121 | } 122 | 123 | public Service getService() { 124 | return service; 125 | } 126 | 127 | public void setService(Service service) { 128 | this.service = service; 129 | } 130 | 131 | public Customer getCustomer() { 132 | return customer; 133 | } 134 | 135 | public void setCustomer(Customer customer) { 136 | this.customer = customer; 137 | } 138 | 139 | public Provider getProvider() { 140 | return provider; 141 | } 142 | 143 | public void setProvider(Provider provider) { 144 | this.provider = provider; 145 | } 146 | 147 | public Consultant getConsultant() { 148 | return consultant; 149 | } 150 | 151 | public void setConsultant(Consultant consultant) { 152 | this.consultant = consultant; 153 | } 154 | 155 | public Long getTradeOrderId() { 156 | return tradeOrderId; 157 | } 158 | 159 | public void setTradeOrderId(Long tradeOrderId) { 160 | this.tradeOrderId = tradeOrderId; 161 | } 162 | 163 | public Byte getTradeType() { 164 | return tradeType; 165 | } 166 | 167 | public void setTradeType(Byte tradeType) { 168 | this.tradeType = tradeType; 169 | } 170 | 171 | public String getPaymentRatio() { 172 | return paymentRatio; 173 | } 174 | 175 | public void setPaymentRatio(String paymentRatio) { 176 | this.paymentRatio = paymentRatio; 177 | } 178 | 179 | public Byte getPaymentTimes() { 180 | return paymentTimes; 181 | } 182 | 183 | public void setPaymentTimes(Byte paymentTimes) { 184 | this.paymentTimes = paymentTimes; 185 | } 186 | 187 | public Long getServiceId() { 188 | return serviceId; 189 | } 190 | 191 | public void setServiceId(Long serviceId) { 192 | this.serviceId = serviceId; 193 | } 194 | 195 | public String getProviderId() { 196 | return providerId; 197 | } 198 | 199 | public void setProviderId(String providerId) { 200 | this.providerId = providerId; 201 | } 202 | 203 | public String getCustomerId() { 204 | return customerId; 205 | } 206 | 207 | public void setCustomerId(String customerId) { 208 | this.customerId = customerId; 209 | } 210 | 211 | public String getConsultantId() { 212 | return consultantId; 213 | } 214 | 215 | public void setConsultantId(String consultantId) { 216 | this.consultantId = consultantId; 217 | } 218 | 219 | public Double getStandardUnitPrice() { 220 | return standardUnitPrice; 221 | } 222 | 223 | public void setStandardUnitPrice(Double standardUnitPrice) { 224 | this.standardUnitPrice = standardUnitPrice; 225 | } 226 | 227 | public Double getActualUnitPrice() { 228 | return actualUnitPrice; 229 | } 230 | 231 | public void setActualUnitPrice(Double actualUnitPrice) { 232 | this.actualUnitPrice = actualUnitPrice; 233 | } 234 | 235 | public Integer getServiceCount() { 236 | return serviceCount; 237 | } 238 | 239 | public void setServiceCount(Integer serviceCount) { 240 | this.serviceCount = serviceCount; 241 | } 242 | 243 | public Double getStandardSumPrice() { 244 | return standardSumPrice; 245 | } 246 | 247 | public void setStandardSumPrice(Double standardSumPrice) { 248 | this.standardSumPrice = standardSumPrice; 249 | } 250 | 251 | public Double getActualSumPrice() { 252 | return actualSumPrice; 253 | } 254 | 255 | public void setActualSumPrice(Double actualSumPrice) { 256 | this.actualSumPrice = actualSumPrice; 257 | } 258 | 259 | public String getAdvancedRemindTime() { 260 | return advancedRemindTime; 261 | } 262 | 263 | public void setAdvancedRemindTime(String advancedRemindTime) { 264 | this.advancedRemindTime = advancedRemindTime; 265 | } 266 | 267 | public String getMandatoryUpdateTime() { 268 | return mandatoryUpdateTime; 269 | } 270 | 271 | public void setMandatoryUpdateTime(String mandatoryUpdateTime) { 272 | this.mandatoryUpdateTime = mandatoryUpdateTime; 273 | } 274 | 275 | public Integer getCurrentProcessNum() { 276 | return currentProcessNum; 277 | } 278 | 279 | public void setCurrentProcessNum(Integer currentProcessNum) { 280 | this.currentProcessNum = currentProcessNum; 281 | } 282 | 283 | public List getPayments() { 284 | return payments; 285 | } 286 | 287 | public void setPayments(List payments) { 288 | this.payments = payments; 289 | } 290 | } -------------------------------------------------------------------------------- /src/test/java/util/MockData.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import bean.*; 4 | import org.apache.commons.io.FileUtils; 5 | 6 | import java.io.*; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * 生成模拟数据 12 | * @author KaiKang 13 | */ 14 | public class MockData { 15 | /** 16 | * 生成模拟数据 17 | * @throws IOException 18 | * @throws ClassNotFoundException 19 | */ 20 | public static void init() throws IOException, ClassNotFoundException { 21 | TradeOrder tradeOrder = new TradeOrder(); 22 | tradeOrder.setTradeOrderId(1l); 23 | tradeOrder.setCustomerId("2123122"); 24 | tradeOrder.setConsultantId("1222"); 25 | tradeOrder.setServiceId(11l); 26 | tradeOrder.setProviderId("233"); 27 | tradeOrder.setActualUnitPrice(2312.0); 28 | tradeOrder.setServiceCount(2); 29 | tradeOrder.setActualSumPrice(4624.0); 30 | tradeOrder.setPaymentRatio("1:3:4"); 31 | tradeOrder.setPaymentTimes((byte) 2); 32 | tradeOrder.setPaymentStatus((byte) -2); 33 | 34 | Customer customer = new Customer(); 35 | customer.setCustomerId("2123122"); 36 | customer.setCustomerName("测试客户"); 37 | customer.setCustomerPhone("2312331231"); 38 | tradeOrder.setCustomer(customer); 39 | 40 | Consultant consultant = new Consultant(); 41 | consultant.setConsultantId("1222"); 42 | consultant.setConsultantPhone("23131221132"); 43 | consultant.setConsultantName("KaiKang"); 44 | tradeOrder.setConsultant(consultant); 45 | 46 | Provider provider = new Provider(); 47 | provider.setProviderId("233"); 48 | provider.setProviderName("测试服务商"); 49 | provider.setLegalPersonPhone("2131332123"); 50 | tradeOrder.setProvider(provider); 51 | 52 | Service service = new Service(); 53 | service.setServiceName("测试服务"); 54 | tradeOrder.setService(service); 55 | 56 | ArrayList payments = new ArrayList<>(); 57 | Payment e = new Payment(); 58 | e.setPaymentWay("1"); 59 | e.setPaymentId(1111l); 60 | e.setPaymentAmount(12.0); 61 | e.setPaymentStatus((byte) 1); 62 | payments.add(e); 63 | Payment e1 = new Payment(); 64 | e1.setPaymentWay("0"); 65 | e1.setPaymentId(1111l); 66 | e1.setPaymentAmount(12.0); 67 | e1.setPaymentStatus((byte) 0); 68 | payments.add(e1); 69 | tradeOrder.setPayments(payments); 70 | 71 | List tradeOrders = new ArrayList<>(); 72 | for(int i=0 ;i<200;i++){ 73 | tradeOrders.add(tradeOrder); 74 | } 75 | System.out.println(tradeOrders.size()); 76 | 77 | File file = new File("/excel/datasource.obj"); 78 | FileOutputStream fileOutputStream = FileUtils.openOutputStream(file); 79 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); 80 | objectOutputStream.writeObject(tradeOrders); 81 | objectOutputStream.flush(); 82 | } 83 | 84 | /** 85 | * 从类路径中获取模拟数据 86 | * @return 87 | * @throws IOException 88 | * @throws ClassNotFoundException 89 | */ 90 | public static List data() throws IOException, ClassNotFoundException { 91 | InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("datasource.obj"); 92 | ObjectInputStream objectInputStream = new ObjectInputStream(resourceAsStream); 93 | return (List) objectInputStream.readObject(); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/util/PaymentWays.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 支付方式Hash存储 8 | * @author KaiKang 9 | */ 10 | public class PaymentWays { 11 | /** 12 | * key,机构号 13 | * value,机构名称 14 | */ 15 | private static final Map paymentWaysMap; 16 | static { 17 | paymentWaysMap = new HashMap(); 18 | paymentWaysMap.put("0", "微信"); 19 | paymentWaysMap.put("1", "支付宝"); 20 | paymentWaysMap.put("700000000000003", "中国工商银行"); 21 | paymentWaysMap.put("700000000000002", "中国农业银行"); 22 | paymentWaysMap.put("700000000000001", "中国银行"); 23 | paymentWaysMap.put("700000000000004", "中国建设银行"); 24 | paymentWaysMap.put("700000000000006", "中国交通银行"); 25 | paymentWaysMap.put("700000000000011", "中国邮政储蓄银行"); 26 | paymentWaysMap.put("700000000000010", "招商银行"); 27 | paymentWaysMap.put("700000000000013", "中信银行"); 28 | paymentWaysMap.put("700000000000015", "华夏银行"); 29 | paymentWaysMap.put("700000000000008", "中国民生银行"); 30 | paymentWaysMap.put("700000000000009", "兴业银行"); 31 | paymentWaysMap.put("700000000000014", "平安银行"); 32 | paymentWaysMap.put("700000000000005", "中国光大银行"); 33 | paymentWaysMap.put("700000000000012", "广发银行"); 34 | paymentWaysMap.put("700000000000007", "浦发银行"); 35 | paymentWaysMap.put("700000000000022", "青岛银行"); 36 | paymentWaysMap.put("700000000000019", "深圳发展银行"); 37 | paymentWaysMap.put("700000000000044", "徽商银行"); 38 | paymentWaysMap.put("700000000000020", "东亚银行"); 39 | } 40 | 41 | /** 42 | * 通过机构号,查询机构名称 43 | * @param bankInstNo 机构号 44 | * @return 45 | */ 46 | public static String bankInstName(String bankInstNo) { 47 | String bankInstName = paymentWaysMap.get(bankInstNo); 48 | return bankInstName == null?"银联支付":bankInstName; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/resources/2003.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuagui/gridexcel/8ea1be715ecd2b92524f2b76a1e9ba27f702c5a0/src/test/resources/2003.xls -------------------------------------------------------------------------------- /src/test/resources/2007.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuagui/gridexcel/8ea1be715ecd2b92524f2b76a1e9ba27f702c5a0/src/test/resources/2007.xlsx -------------------------------------------------------------------------------- /src/test/resources/datasource.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuagui/gridexcel/8ea1be715ecd2b92524f2b76a1e9ba27f702c5a0/src/test/resources/datasource.obj -------------------------------------------------------------------------------- /src/test/resources/test.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuagui/gridexcel/8ea1be715ecd2b92524f2b76a1e9ba27f702c5a0/src/test/resources/test.xls -------------------------------------------------------------------------------- /src/test/resources/test.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liuhuagui/gridexcel/8ea1be715ecd2b92524f2b76a1e9ba27f702c5a0/src/test/resources/test.xlsx --------------------------------------------------------------------------------