反射+自定义注解---实现Excel数据列属性和JavaBean属性的自动映射

简单粗暴,直奔主题。

     需求:通过自定义注解和反射技术,将Excel文件中的数据自动映射到pojo类中,最终返回一个List<pojo>集合?

  今天我只是通过一位使用者的身份来给各位分享一套超级可以的POI“工具”,这套工具我只是第一个使用者,创作者是我的朋友,他喜好钻研底层和算法,擅长计算机软硬件,在我心里他一直是神一样的存在,每天晚上10点后我才能看到他,因为他每天需要加班,需要有更多时间能够学习,唉,这种毅力和耐力,我是真的羡慕,因为我也一直在努力,能够得到更多的东西。

  首先关于jar的管理,我就不多说了,导入和POI相关的jar包即可。第一我给大家分享的是一个他封装好的工具类,原理是通过获取到Excel文件,然后通过你指定的pojo对象,他就会自动封装。这套代码也就400行左右,说真的用点心瞅瞅完全有必要看懂,不多说了,我看了半天,自己也能说得通他是怎么写的,更详细的我也想给各位补补,但是无能为力啊。

  1 public class ExcelUtil {
  2
  3     public String defaultDateFormat = "yyyy-MM-dd HH:mm:ss";
  4
  5     /**
  6      * 将excel表格中的信息设置进bean中
  7      *
  8      * @param file
  9      * @param t
 10      * @return
 11      * @throws Exception
 12      * @Date 2017年6月13日
 13      */
 14     public <T> T setExcelInfo2Bean(File file, T t) {
 15         // 获取工作簿类下的子类(表类)
 16         Field[] declaredFields = t.getClass().getDeclaredFields();
 17
 18         for (int i = 0; i < declaredFields.length; i++) {
 19             Field sheetFiled = declaredFields[i];
 20             sheetFiled.setAccessible(true);
 21             // 将子表的内容赋值到对象中
 22             try {
 23                 sheetFiled.set(t, setSheetValue2Bean(sheetFiled, file));
 24             } catch (Exception e) {
 25                 e.printStackTrace();
 26             }
 27         }
 28         return t;
 29     }
 30
 31     /**
 32      * 校验参数的类中是否包含ExcelSheetName注解
 33      *
 34      * @param declaredFields
 35      * @Date 2017年6月13日
 36      */
 37     public <T extends Annotation> Field[] matchDeclaredFields(Field[] declaredFields, Class T) {
 38         List<Field> matchedDeclaredFieldsList = new ArrayList<Field>();
 39
 40         for (int i = 0; i < declaredFields.length; i++) {
 41
 42             Field sheetFiled = declaredFields[i];
 43             sheetFiled.setAccessible(true);
 44             if (sheetFiled.getAnnotation(T) != null) {
 45                 matchedDeclaredFieldsList.add(sheetFiled);
 46             }
 47         }
 48         Field[] matchedDeclaredFieldsArray = null;
 49
 50         if (matchedDeclaredFieldsList.size() > 0) {
 51             matchedDeclaredFieldsArray = new Field[matchedDeclaredFieldsList.size()];
 52
 53             for (int i = 0; i < matchedDeclaredFieldsArray.length; i++) {
 54                 matchedDeclaredFieldsArray[i] = matchedDeclaredFieldsList.get(i);
 55             }
 56         }
 57
 58         return matchedDeclaredFieldsArray;
 59
 60     }
 61
 62     /**
 63      * 将子表的内容赋值到对象中
 64      *
 65      * @param sheetFiled
 66      * @param file
 67      * @return
 68      * @throws Exception
 69      * @Date 2017年6月8日
 70      */
 71     private <T> Object setSheetValue2Bean(Field sheetFiled, File file) throws Exception {
 72         // 薄类中所有参数均为list类型,不进行校验
 73         Class sheetListClass = sheetFiled.getType();
 74         // 创建集合对象
 75         // List sheetList = (List) sheetListClass.newInstance();
 76         // 获取参数的类型的参数化的类型
 77         Type type = sheetFiled.getGenericType();
 78         // 将参数化的类型强转,获得类型中的参数(泛型中的类)
 79         ParameterizedType pType = (ParameterizedType) type;
 80         // 泛型中的参数,如果是map,数组长度就为2
 81         Type[] listType = pType.getActualTypeArguments();
 82         // 获取list泛型中的子表class
 83         Class sheetClass = (Class) listType[0];
 84
 85         // 获取子类对应的sheet名
 86         ExcelSheetName sheetNameAnno = (ExcelSheetName) sheetClass.getAnnotation(ExcelSheetName.class);
 87
 88         String sheetName = sheetNameAnno.value();
 89
 90         // 获取文件后缀
 91         String fileExt = file.getName().substring(file.getName().lastIndexOf(".") + 1);
 92         // 创建流
 93         InputStream input = new FileInputStream(file);
 94
 95         // 创建Workbook
 96         Workbook wb = null;
 97
 98         // 创建sheet
 99         Sheet sheet = null;
100
101         // 根据后缀判断excel 2003 or 2007+
102         if (fileExt.equals("xls")) {
103             wb = (HSSFWorkbook) WorkbookFactory.create(input);
104         } else {
105             wb = new XSSFWorkbook(input);
106         }
107
108         // 获取表
109         sheet = wb.getSheet(sheetName);
110         // 获取行数
111
112         return getExcelInfo2Bean(sheetClass, sheet);
113     }
114
115     /**
116      * 将返回与sheet内容对应的class的实例的List集合
117      *
118      * @param sheetClass
119      * @param sheet
120      * @throws Exception
121      * @Date 2017年6月13日
122      */
123     private <T extends ExcelCheckPropertie> List<T> getExcelInfo2Bean(Class T, Sheet sheet) throws Exception {
124         Map<String, Integer> cellNameMap = getCellNameMap(sheet);
125
126         // 获取行数
127         int rowNum = sheet.getLastRowNum();
128         if (rowNum == 0) {
129             return new ArrayList<T>();
130         }
131
132         List<T> tList = new ArrayList<T>(rowNum - 1);
133
134         // 获取子表类的属性(对应表中的列)
135         Field[] colFields = T.getDeclaredFields();
136         Field[] excelCheckPropertiesDeclaredFields = T.getSuperclass().getDeclaredFields();
137         // (获取只包含自定义注解的属性)
138         Field[] matchedColFields = matchDeclaredFields(colFields, ExcelColName.class);
139         // 如果包含自定义注解的参数
140
141         // 从第二行开始读取,并设置进实例
142         for (int j = 1; j <= rowNum; j++) {
143             Row row = sheet.getRow(j);
144             if (row == null) {
145                 continue;
146             }
147             // 创建当前sheet类的实例
148             T sheetBean = (T) T.newInstance();
149
150             // 遍历包含自定义注解的参数
151             if (matchedColFields != null && matchedColFields.length > 0) {
152
153                 for (int i = 0; i < matchedColFields.length; i++) {
154                     matchedColFields[i].setAccessible(true);
155                     Field colField = matchedColFields[i];
156
157                     ExcelColName excelColNameAnno = colField.getAnnotation(ExcelColName.class);
158                     String excelColName = excelColNameAnno.value().trim();
159                     // 判断该参数是否需要校验
160                     boolean isRequired = excelColNameAnno.IsRequired();
161                     // 如果为必填字段
162                     if (isRequired) {
163                         // 遍历每行的每个参数,设置进bean
164                         for (int k = 0; k < row.getPhysicalNumberOfCells(); k++) {
165
166                             // 获取sheet类的属性对应的表中的列的cell对象
167                             Cell cell = row.getCell(cellNameMap.get(excelColName));
168                             String cellValue = "";
169                             if (cell != null) {
170                                 cellValue = getCellValue(cell);
171
172                                 // 判断属性类型
173                                 if (matchedColFields[i].getType().isAssignableFrom(Integer.class)) {
174                                     matchedColFields[i].set(sheetBean, Integer.parseInt(getCellValue(cell)));
175
176                                 } else if (matchedColFields[i].getType().isAssignableFrom(Date.class)) {
177                                     matchedColFields[i].set(sheetBean, getDateCellValue(cell));
178
179                                 } else if (matchedColFields[i].getType().isAssignableFrom(Double.class)) {
180                                     matchedColFields[i].set(sheetBean, Double.parseDouble(getCellValue(cell)));
181
182                                 } else if (matchedColFields[i].getType().isAssignableFrom(Float.class)) {
183                                     matchedColFields[i].set(sheetBean, Float.parseFloat(getCellValue(cell)));
184
185                                 } else {
186                                     matchedColFields[i].set(sheetBean, getCellValue(cell));
187                                 }
188                             }
189
190                             // 设置父类属性
191                             for (int l = 0; l < excelCheckPropertiesDeclaredFields.length; l++) {
192                                 Field superField = excelCheckPropertiesDeclaredFields[l];
193                                 superField.setAccessible(true);
194                                 // 当前单元格所在表名
195                                 if (superField.getName().equals("sheetName")) {
196                                     superField.set(sheetBean, sheet.getSheetName());
197                                     // 当前单元格所在行数
198                                 } else if (superField.getName().equals("rowNum")) {
199                                     superField.set(sheetBean, j);
200                                     // 当前单元格所在列名
201                                 } else if (superField.getName().equals("colName")) {
202                                     superField.set(sheetBean, excelColName);
203                                     // 非空校验结果
204                                 } else if (superField.getName().equals("isChecked")) {
205                                     if (cellValue == null || "".equals(cellValue.trim())) {
206                                         superField.set(sheetBean, false);
207                                     }
208
209                                 }
210                             }
211
212                         }
213                     } else {
214                         // 遍历每行的每个参数,设置进bean
215                         for (int k = 0; k < row.getPhysicalNumberOfCells(); k++) {
216
217                             // 获取sheet类的属性对应的表中的列的cell对象
218                             if (excelColName.equals("上传时间")) {
219                                 System.out.println();
220                             }
221                             Integer integer = cellNameMap.get(excelColName);
222                             Cell cell = row.getCell(integer);
223                             if (cell != null) {
224                                 // 设置父类属性
225                                 for (int l = 0; l < excelCheckPropertiesDeclaredFields.length; l++) {
226                                     Field superField = excelCheckPropertiesDeclaredFields[l];
227                                     superField.setAccessible(true);
228                                     // 当前单元格所在表名
229                                     if (superField.getName().equals("sheetName")) {
230                                         superField.set(sheetBean, sheet.getSheetName());
231                                         // 当前单元格所在行数
232                                     } else if (superField.getName().equals("rowNum")) {
233                                         superField.set(sheetBean, j);
234                                         // 当前单元格所在列名
235                                     } else if (superField.getName().equals("colName")) {
236                                         superField.set(sheetBean, excelColName);
237                                     }
238                                 }
239                                 // 判断属性类型
240                                 if (matchedColFields[i].getType().isAssignableFrom(Integer.class)) {
241                                     matchedColFields[i].set(sheetBean, Integer.parseInt(getCellValue(cell)));
242
243                                 } else if (matchedColFields[i].getType().isAssignableFrom(Date.class)) {
244                                     matchedColFields[i].set(sheetBean, getDateCellValue(cell));
245
246                                 } else if (matchedColFields[i].getType().isAssignableFrom(Double.class)) {
247                                     matchedColFields[i].set(sheetBean, Double.parseDouble(getCellValue(cell)));
248
249                                 } else if (matchedColFields[i].getType().isAssignableFrom(Float.class)) {
250                                     matchedColFields[i].set(sheetBean, Float.parseFloat(getCellValue(cell)));
251
252                                 } else {
253                                     matchedColFields[i].set(sheetBean, getCellValue(cell));
254                                 }
255                             }
256                         }
257                     }
258
259                 }
260             }
261             tList.add(sheetBean);
262         }
263
264         // 校验空值
265         ListIterator<T> listIterator = tList.listIterator();
266         while (listIterator.hasNext()) {
267             T next = listIterator.next();
268             int nullNum = 0;
269             for (int i = 0; i < matchedColFields.length; i++) {
270                 if (matchedColFields[i].get(next) == null || matchedColFields[i].get(next).toString().equals("")) {
271                     ++nullNum;
272                 }
273             }
274             if (nullNum == matchedColFields.length) {
275                 // System.out.println("已删除一个元素");
276                 listIterator.remove();
277             }
278         }
279
280         return tList;
281
282     }
283
284     /**
285      * 获取时间类型数值 cell.getCellStyle().getDataFormat() 日期时间(yyyy-MM-dd HH:mm:ss) -
286      * 22, 日期(yyyy-MM-dd) - 14, 时间(HH:mm:ss) - 21, 年月(yyyy-MM) - 17, 时分(HH:mm) -
287      * 20, 月日(MM-dd) - 58
288      *
289      * @param cell
290      * @return
291      * @Date 2017年6月13日
292      */
293     private Date getDateCellValue(Cell cell) {
294         return cell.getDateCellValue();
295     }
296
297     /**
298      * 获取第一行做标题存入列名与对应的列值
299      *
300      * @param sheet
301      * @return
302      * @Date 2017年6月13日
303      */
304     public Map<String, Integer> getCellNameMap(Sheet sheet) {
305         // 获取第一行列的列名及列数存入map
306         Map<String, Integer> colNameMap = new HashMap<String, Integer>();
307         Row firstRow = sheet.getRow(0);
308         // 列数
309         int cellNum = firstRow.getLastCellNum();
310         // map赋值
311         for (int i = 0; i < cellNum; i++) {
312             colNameMap.put(getCellValue(firstRow.getCell(i)), i);
313         }
314
315         return colNameMap;
316     }
317
318     /**
319      * 对Excel的各个单元格的格式进行判断并转换
320      */
321     private String getCellValue(Cell cell) {
322         String cellValue = "";
323         DecimalFormat df = new DecimalFormat("####################.##########");
324         switch (cell.getCellType()) {
325         case HSSFCell.CELL_TYPE_STRING:
326             cellValue = cell.getRichStringCellValue().getString().trim();
327             break;
328         case HSSFCell.CELL_TYPE_NUMERIC:
329             if (HSSFDateUtil.isCellDateFormatted(cell)) {
330                 Date date = cell.getDateCellValue();
331                 cellValue = new SimpleDateFormat(defaultDateFormat).format(date);
332             } else {
333                 double dc = cell.getNumericCellValue();
334                 // cellValue = String.valueOf(dc);
335                 cellValue = df.format(dc);
336             }
337             break;
338         case HSSFCell.CELL_TYPE_BOOLEAN:
339             cellValue = String.valueOf(cell.getBooleanCellValue()).trim();
340             break;
341         case HSSFCell.CELL_TYPE_FORMULA:
342             cellValue = String.valueOf(cell.getNumericCellValue());
343             break;
344
345         default:
346             cellValue = "";
347         }
348         return cellValue;
349     }
350
351 }

工具类

  接着就是俩个自定义注解分别是:@ExcelSheetName@ExcelColName,这俩个注解都是放在pojo类上的。第一个主要是标注和Excel文件中那张sheet表,第二个主要是将Excel文件中的列名和pojo类的对应属性绑定,具体用法瞅瞅我下面贴的代码就OK。

 1 /**
 2  * 用于匹配Excel文件中的sheet表名(注解值必须是Excel文件中sheet表名)
 3  * @author zxz
 4  *
 5  */
 6 @Documented
 7 @Inherited
 8 @Retention(RetentionPolicy.RUNTIME)
 9 public @interface ExcelSheetName {
10     String value() default "";
11 }

 1 /**
 2  * 用于匹配Excel表中的列(vlaue值必须是Excel文件中第一行的列名)
 3  * @author zxz
 4  *
 5  */
 6 @Documented
 7 @Target(ElementType.FIELD)
 8 @Inherited
 9 @Retention(RetentionPolicy.RUNTIME)
10 public @interface ExcelColName {
11     String value() default "";
12     boolean IsRequired() default false;
13 }

  具体是如何使用自定义注解将pojo类和Excel文件中的数据完成自动映射的,请参考下面pojo类代码。

 1 /**
 2  * 商品
 3  * @author zxz
 4  *
 5  */
 6 @ExcelSheetName("商品信息")
 7 public class Item extends ExcelCheckPropertie implements Serializable {
 8
 9     private String id;                //主键
10     @ExcelColName(value="商品名称",IsRequired=true)        //IsRequired=true表示非空
11     private String itemName;        //商品名称
12     @ExcelColName(value="价格")
13     private Double price;            //价格
14     @ExcelColName(value="描述")
15     private String itemDesc;        //描述
16     @DateTimeFormat(pattern="yyyy-MM-dd")
17     @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
18     @ExcelColName(value="上架时间")
19     private Date createTime;        //添加时间

Item

  最后,我是将这套东西整合到我的一个数据录入小项目中,因为之前导入一张600条数据的文件时,速度就很慢,一直是我的心头病,不过这次杠杠的。那天下午我整合成功后,心里一直乐到下班,因为最后进行了一套小小的性能和速度测试,结果美滋滋。我调用工具类中的方法进行数据的自动映射,数据10000条,最终导入到数据库中全程使用了7分钟,各位是不是觉得时间还是有点长,但是这个过程我是即把这10000多条的数据封装进来了而且还成功插入到数据库中去了,我想这个结果应该能及格吧,如果各位还不能接受这个速度,那可以优化数据库的读写速度,效果可能会更好。需要特别说明一点的是:将Excel文件中的数据封装到数据集合中只需3秒多一点,我反正是够用了,哈哈~~

  我的数据最后是封装到一个结果处理Vo类中。

 1 import java.io.Serializable;
 2 import java.util.List;
 3
 4 public class ItemVo implements Serializable {
 5
 6     private List<Item> listItem;
 7
 8     public List<Item> getListItem() {
 9         return listItem;
10     }
11
12     public void setListItem(List<Item> listItem) {
13         this.listItem = listItem;
14     }
15
16     @Override
17     public String toString() {
18         return "ItemVo [listItem=" + listItem + "]";
19     }
20 }

ItemVo

 1 @Controller
 2 @RequestMapping("/poi")
 3 public class MainPOIAction {
 4
 5     @Autowired
 6     private ItemService itemService;
 7
 8     /**
 9      * 自动映射Excel文件和javaBean对象的属性封装
10      * @return
11      */
12     @RequestMapping(value = "/autoMapping",produces = "text/plain;charset=UTF-8")
13     @ResponseBody
14     public String autoMapping(){
15         ExcelUtil eu = new ExcelUtil();
16         // 开始导入时间
17         long starTime=System.nanoTime();
18         // 将指定路径下Excel文件中的数据自动封装到Bean对象中
19         ItemVo itemVo = eu.setExcelInfo2Bean(new File("H://POI开发//商品信息模板.xlsx"), new ItemVo());
20         List<Item> listItem = itemVo.getListItem();
21         /*for (Item item : listItem) {
22             int save = itemService.saveItem(item);
23             if(save != 1){
24                 System.out.println("商品ID"+item.getId()+"导入失败");
25                 continue;
26             }
27         }*/
28         // 导入结束时间
29         long endTime=System.nanoTime();
30         long time = endTime-starTime;
31         return JsonUtil.object2Json(time);
32     }
33
34 }

main

  纯属抱大腿,但是也学到了不少东西,希望能给各位博友带来灵感。

时间: 2024-09-30 18:54:42

反射+自定义注解---实现Excel数据列属性和JavaBean属性的自动映射的相关文章

JAVA里自定义注解来进行数据验证

API开发中经常会遇到一些对请求数据进行验证的情况,这时候如果使用注解就有两个好处,一是验证逻辑和业务逻辑分离,代码清晰,二是验证逻辑可以轻松复用,只需要在要验证的地方加上注解就可以. Java提供了一些基本的验证注解,比如@NotNull.@Size,但是更多情况下需要自定义验证逻辑,这时候就可以自己实现一个验证注解,方法很简单,仅需要两个东西: 一个自定义的注解,并且指定验证器 一个验证器的实现 自定义验证注解 考虑有一个API,接收一个Student对象,并希望对象里的age域的值是奇数,

Python处理Excel(四):自定义类处理Excel数据

完整代码如下,有时间再总结细节: #coding=utf-8 import xlrd import xlwt from xlutils.copy import copy from copy import deepcopy class DataOutput_Helper: '''It's a data structure about scenario. there are many methods provioded to make output the excel data easily'''

上传Excel数据到数据库中(Asp.net自动生成三层代码 第六集 )

上传文件 string savePath = Server.MapPath("~/upload/");//指定上传文件在服务器上的保存路径 //检查服务器上是否存在这个物理路径,如果不存在则创建 if (!System.IO.Directory.Exists(savePath)) { System.IO.Directory.CreateDirectory(savePath); } if (FileUpload1.HasFile) { string fileExtension = Pat

如何正确选择MySQL数据列类型

MySQL数据列类型选择是在我们设计表的时候经常会遇到的问题,下面就教您如何正确选择MySQL数据列类型,供您参考学习. 选择正确的数据列类型能大大提高数据库的性能和使数据库具有高扩展性.在选择MySQL数据列类型时,请从以下几个方面考虑: 存放到数据列中的数据类型. 数据值的取值范围. 考虑性能和处理效率. 数值操作比字符操作快. 小类型的处理速度比大类型快. 不同数据表中固定长度类型和可变长度类型的处理效率是不同的. 可变长度类型在经过删除和修改操作后容易产生碎片,降低系统性能,需定期运行O

Java自定义注解反射校验数据

package com.annotations.ecargo; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUN

利用java反射机制实现读取excel表格中的数据

如果直接把excel表格中的数据导入数据库,首先应该将excel中的数据读取出来. 为了实现代码重用,所以使用了Object,而最终的结果是要获取一个list如List<User>.List<Book>等,所以需要使用泛型机制去实现.下面会给出代码,可能会稍微复杂一点,但注释很清晰,希望大家耐心阅读. 在上代码之前简单说一下思路: 1.excel表格必须有表头,且表头中各列的值要与实体类的属性相同: 2.先读取表头信息,然后获取表头列数,接着确定需要使用的set方法的名称,并存到数

NPOI操作excel——利用反射机制,NPOI读取excel数据准确映射到数据库字段

> 其实需求很明确,就是一大堆不一样的excel,每张excel对应数据库的一张表,我们需要提供用户上传excel,我们解析数据入库的功能实现. 那么,这就涉及到一个问题:我们可以读出excel的表头,但是怎么知道每个表头具体对应数据库里面的字段呢? 博主经过一段时间的思考与构思,想到一法:现在的情况是我们有excel表A,对应数据库表B,但是A与B具体属性字段的映射关系我们不知.那我们是不是可以有一个A到B的映射文件C呢? 我想,说到这,大家就很明了了... 第一步:为每张excel创建一个与

使用Java反射(Reflect)、自定义注解(Customer Annotation)生成简单SQL语句

这次给大家介绍一下在Java开发过程中 使用自定义注解开发:主要知识点:            1.反射            主要用于提取注解信息            2.自定义异常  主要是为了自己自定义一个异常信息            3.自定义注解  本次重点 学会如何自定义注解以及如何使用反射提取注解信息运用到实际开发下图表示在Java中注解的含义以及注解的分类和如何解析注解 通常我们使用自定义注解一般使用4中元注解即:@Target@Retention@Documented@In

【动态页面】(三)之二:通过自定义注解读取Jar包的类名和属性名

上篇博客介绍了通过反射读取Jar包的类名和属性名,但是问题是读不出类名和属性名的中文注释和属性类型.所以上篇博客埋下了一个伏笔,就是通过自定义注解的方式读取Jar包的类名.属性名.中文注释和属性类型.这篇博客我们就来好好讲讲是如何实现的. 首先先说一下,由于我们的Jar包没有放到项目下,所以就想着怎么能把Jar包添加进来,所以先做了一个类似于上传文件的功能,将Jar上传进来,然后再读取Jar包里面的类名.属性名等一系列属性,再添加到数据库中.总体的思路确定了,下面就开始实施. 首先是上传Jar包