简单粗暴,直奔主题。

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

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

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

 public class ExcelUtil {

     public String defaultDateFormat = "yyyy-MM-dd HH:mm:ss";

     /**
* 将excel表格中的信息设置进bean中
*
* @param file
* @param t
* @return
* @throws Exception
* @Date 2017年6月13日
*/
public <T> T setExcelInfo2Bean(File file, T t) {
// 获取工作簿类下的子类(表类)
Field[] declaredFields = t.getClass().getDeclaredFields(); for (int i = 0; i < declaredFields.length; i++) {
Field sheetFiled = declaredFields[i];
sheetFiled.setAccessible(true);
// 将子表的内容赋值到对象中
try {
sheetFiled.set(t, setSheetValue2Bean(sheetFiled, file));
} catch (Exception e) {
e.printStackTrace();
}
}
return t;
} /**
* 校验参数的类中是否包含ExcelSheetName注解
*
* @param declaredFields
* @Date 2017年6月13日
*/
public <T extends Annotation> Field[] matchDeclaredFields(Field[] declaredFields, Class T) {
List<Field> matchedDeclaredFieldsList = new ArrayList<Field>(); for (int i = 0; i < declaredFields.length; i++) { Field sheetFiled = declaredFields[i];
sheetFiled.setAccessible(true);
if (sheetFiled.getAnnotation(T) != null) {
matchedDeclaredFieldsList.add(sheetFiled);
}
}
Field[] matchedDeclaredFieldsArray = null; if (matchedDeclaredFieldsList.size() > 0) {
matchedDeclaredFieldsArray = new Field[matchedDeclaredFieldsList.size()]; for (int i = 0; i < matchedDeclaredFieldsArray.length; i++) {
matchedDeclaredFieldsArray[i] = matchedDeclaredFieldsList.get(i);
}
} return matchedDeclaredFieldsArray; } /**
* 将子表的内容赋值到对象中
*
* @param sheetFiled
* @param file
* @return
* @throws Exception
* @Date 2017年6月8日
*/
private <T> Object setSheetValue2Bean(Field sheetFiled, File file) throws Exception {
// 薄类中所有参数均为list类型,不进行校验
Class sheetListClass = sheetFiled.getType();
// 创建集合对象
// List sheetList = (List) sheetListClass.newInstance();
// 获取参数的类型的参数化的类型
Type type = sheetFiled.getGenericType();
// 将参数化的类型强转,获得类型中的参数(泛型中的类)
ParameterizedType pType = (ParameterizedType) type;
// 泛型中的参数,如果是map,数组长度就为2
Type[] listType = pType.getActualTypeArguments();
// 获取list泛型中的子表class
Class sheetClass = (Class) listType[0]; // 获取子类对应的sheet名
ExcelSheetName sheetNameAnno = (ExcelSheetName) sheetClass.getAnnotation(ExcelSheetName.class); String sheetName = sheetNameAnno.value(); // 获取文件后缀
String fileExt = file.getName().substring(file.getName().lastIndexOf(".") + 1);
// 创建流
InputStream input = new FileInputStream(file); // 创建Workbook
Workbook wb = null; // 创建sheet
Sheet sheet = null; // 根据后缀判断excel 2003 or 2007+
if (fileExt.equals("xls")) {
wb = (HSSFWorkbook) WorkbookFactory.create(input);
} else {
wb = new XSSFWorkbook(input);
} // 获取表
sheet = wb.getSheet(sheetName);
// 获取行数 return getExcelInfo2Bean(sheetClass, sheet);
} /**
* 将返回与sheet内容对应的class的实例的List集合
*
* @param sheetClass
* @param sheet
* @throws Exception
* @Date 2017年6月13日
*/
private <T extends ExcelCheckPropertie> List<T> getExcelInfo2Bean(Class T, Sheet sheet) throws Exception {
Map<String, Integer> cellNameMap = getCellNameMap(sheet); // 获取行数
int rowNum = sheet.getLastRowNum();
if (rowNum == 0) {
return new ArrayList<T>();
} List<T> tList = new ArrayList<T>(rowNum - 1); // 获取子表类的属性(对应表中的列)
Field[] colFields = T.getDeclaredFields();
Field[] excelCheckPropertiesDeclaredFields = T.getSuperclass().getDeclaredFields();
// (获取只包含自定义注解的属性)
Field[] matchedColFields = matchDeclaredFields(colFields, ExcelColName.class);
// 如果包含自定义注解的参数 // 从第二行开始读取,并设置进实例
for (int j = 1; j <= rowNum; j++) {
Row row = sheet.getRow(j);
if (row == null) {
continue;
}
// 创建当前sheet类的实例
T sheetBean = (T) T.newInstance(); // 遍历包含自定义注解的参数
if (matchedColFields != null && matchedColFields.length > 0) { for (int i = 0; i < matchedColFields.length; i++) {
matchedColFields[i].setAccessible(true);
Field colField = matchedColFields[i]; ExcelColName excelColNameAnno = colField.getAnnotation(ExcelColName.class);
String excelColName = excelColNameAnno.value().trim();
// 判断该参数是否需要校验
boolean isRequired = excelColNameAnno.IsRequired();
// 如果为必填字段
if (isRequired) {
// 遍历每行的每个参数,设置进bean
for (int k = 0; k < row.getPhysicalNumberOfCells(); k++) { // 获取sheet类的属性对应的表中的列的cell对象
Cell cell = row.getCell(cellNameMap.get(excelColName));
String cellValue = "";
if (cell != null) {
cellValue = getCellValue(cell); // 判断属性类型
if (matchedColFields[i].getType().isAssignableFrom(Integer.class)) {
matchedColFields[i].set(sheetBean, Integer.parseInt(getCellValue(cell))); } else if (matchedColFields[i].getType().isAssignableFrom(Date.class)) {
matchedColFields[i].set(sheetBean, getDateCellValue(cell)); } else if (matchedColFields[i].getType().isAssignableFrom(Double.class)) {
matchedColFields[i].set(sheetBean, Double.parseDouble(getCellValue(cell))); } else if (matchedColFields[i].getType().isAssignableFrom(Float.class)) {
matchedColFields[i].set(sheetBean, Float.parseFloat(getCellValue(cell))); } else {
matchedColFields[i].set(sheetBean, getCellValue(cell));
}
} // 设置父类属性
for (int l = 0; l < excelCheckPropertiesDeclaredFields.length; l++) {
Field superField = excelCheckPropertiesDeclaredFields[l];
superField.setAccessible(true);
// 当前单元格所在表名
if (superField.getName().equals("sheetName")) {
superField.set(sheetBean, sheet.getSheetName());
// 当前单元格所在行数
} else if (superField.getName().equals("rowNum")) {
superField.set(sheetBean, j);
// 当前单元格所在列名
} else if (superField.getName().equals("colName")) {
superField.set(sheetBean, excelColName);
// 非空校验结果
} else if (superField.getName().equals("isChecked")) {
if (cellValue == null || "".equals(cellValue.trim())) {
superField.set(sheetBean, false);
} }
} }
} else {
// 遍历每行的每个参数,设置进bean
for (int k = 0; k < row.getPhysicalNumberOfCells(); k++) { // 获取sheet类的属性对应的表中的列的cell对象
if (excelColName.equals("上传时间")) {
System.out.println();
}
Integer integer = cellNameMap.get(excelColName);
Cell cell = row.getCell(integer);
if (cell != null) {
// 设置父类属性
for (int l = 0; l < excelCheckPropertiesDeclaredFields.length; l++) {
Field superField = excelCheckPropertiesDeclaredFields[l];
superField.setAccessible(true);
// 当前单元格所在表名
if (superField.getName().equals("sheetName")) {
superField.set(sheetBean, sheet.getSheetName());
// 当前单元格所在行数
} else if (superField.getName().equals("rowNum")) {
superField.set(sheetBean, j);
// 当前单元格所在列名
} else if (superField.getName().equals("colName")) {
superField.set(sheetBean, excelColName);
}
}
// 判断属性类型
if (matchedColFields[i].getType().isAssignableFrom(Integer.class)) {
matchedColFields[i].set(sheetBean, Integer.parseInt(getCellValue(cell))); } else if (matchedColFields[i].getType().isAssignableFrom(Date.class)) {
matchedColFields[i].set(sheetBean, getDateCellValue(cell)); } else if (matchedColFields[i].getType().isAssignableFrom(Double.class)) {
matchedColFields[i].set(sheetBean, Double.parseDouble(getCellValue(cell))); } else if (matchedColFields[i].getType().isAssignableFrom(Float.class)) {
matchedColFields[i].set(sheetBean, Float.parseFloat(getCellValue(cell))); } else {
matchedColFields[i].set(sheetBean, getCellValue(cell));
}
}
}
} }
}
tList.add(sheetBean);
} // 校验空值
ListIterator<T> listIterator = tList.listIterator();
while (listIterator.hasNext()) {
T next = listIterator.next();
int nullNum = 0;
for (int i = 0; i < matchedColFields.length; i++) {
if (matchedColFields[i].get(next) == null || matchedColFields[i].get(next).toString().equals("")) {
++nullNum;
}
}
if (nullNum == matchedColFields.length) {
// System.out.println("已删除一个元素");
listIterator.remove();
}
} return tList; } /**
* 获取时间类型数值 cell.getCellStyle().getDataFormat() 日期时间(yyyy-MM-dd HH:mm:ss) -
* 22, 日期(yyyy-MM-dd) - 14, 时间(HH:mm:ss) - 21, 年月(yyyy-MM) - 17, 时分(HH:mm) -
* 20, 月日(MM-dd) - 58
*
* @param cell
* @return
* @Date 2017年6月13日
*/
private Date getDateCellValue(Cell cell) {
return cell.getDateCellValue();
} /**
* 获取第一行做标题存入列名与对应的列值
*
* @param sheet
* @return
* @Date 2017年6月13日
*/
public Map<String, Integer> getCellNameMap(Sheet sheet) {
// 获取第一行列的列名及列数存入map
Map<String, Integer> colNameMap = new HashMap<String, Integer>();
Row firstRow = sheet.getRow(0);
// 列数
int cellNum = firstRow.getLastCellNum();
// map赋值
for (int i = 0; i < cellNum; i++) {
colNameMap.put(getCellValue(firstRow.getCell(i)), i);
} return colNameMap;
} /**
* 对Excel的各个单元格的格式进行判断并转换
*/
private String getCellValue(Cell cell) {
String cellValue = "";
DecimalFormat df = new DecimalFormat("####################.##########");
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_STRING:
cellValue = cell.getRichStringCellValue().getString().trim();
break;
case HSSFCell.CELL_TYPE_NUMERIC:
if (HSSFDateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
cellValue = new SimpleDateFormat(defaultDateFormat).format(date);
} else {
double dc = cell.getNumericCellValue();
// cellValue = String.valueOf(dc);
cellValue = df.format(dc);
}
break;
case HSSFCell.CELL_TYPE_BOOLEAN:
cellValue = String.valueOf(cell.getBooleanCellValue()).trim();
break;
case HSSFCell.CELL_TYPE_FORMULA:
cellValue = String.valueOf(cell.getNumericCellValue());
break; default:
cellValue = "";
}
return cellValue;
} }

工具类

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

 /**
* 用于匹配Excel文件中的sheet表名(注解值必须是Excel文件中sheet表名)
* @author zxz
*
*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelSheetName {
String value() default "";
}
 /**
* 用于匹配Excel表中的列(vlaue值必须是Excel文件中第一行的列名)
* @author zxz
*
*/
@Documented
@Target(ElementType.FIELD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelColName {
String value() default "";
boolean IsRequired() default false;
}

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

 /**
* 商品
* @author zxz
*
*/
@ExcelSheetName("商品信息")
public class Item extends ExcelCheckPropertie implements Serializable { private String id; //主键
@ExcelColName(value="商品名称",IsRequired=true) //IsRequired=true表示非空
private String itemName; //商品名称
@ExcelColName(value="价格")
private Double price; //价格
@ExcelColName(value="描述")
private String itemDesc; //描述
@DateTimeFormat(pattern="yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@ExcelColName(value="上架时间")
private Date createTime; //添加时间

Item

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

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

 import java.io.Serializable;
import java.util.List; public class ItemVo implements Serializable { private List<Item> listItem; public List<Item> getListItem() {
return listItem;
} public void setListItem(List<Item> listItem) {
this.listItem = listItem;
} @Override
public String toString() {
return "ItemVo [listItem=" + listItem + "]";
}
}

ItemVo

 @Controller
@RequestMapping("/poi")
public class MainPOIAction { @Autowired
private ItemService itemService; /**
* 自动映射Excel文件和javaBean对象的属性封装
* @return
*/
@RequestMapping(value = "/autoMapping",produces = "text/plain;charset=UTF-8")
@ResponseBody
public String autoMapping(){
ExcelUtil eu = new ExcelUtil();
// 开始导入时间
long starTime=System.nanoTime();
// 将指定路径下Excel文件中的数据自动封装到Bean对象中
ItemVo itemVo = eu.setExcelInfo2Bean(new File("H://POI开发//商品信息模板.xlsx"), new ItemVo());
List<Item> listItem = itemVo.getListItem();
/*for (Item item : listItem) {
int save = itemService.saveItem(item);
if(save != 1){
System.out.println("商品ID"+item.getId()+"导入失败");
continue;
}
}*/
// 导入结束时间
long endTime=System.nanoTime();
long time = endTime-starTime;
return JsonUtil.object2Json(time);
} }

main

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

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

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

    API开发中经常会遇到一些对请求数据进行验证的情况,这时候如果使用注解就有两个好处,一是验证逻辑和业务逻辑分离,代码清晰,二是验证逻辑可以轻松复用,只需要在要验证的地方加上注解就可以. Java提供了 ...

  2. 在 Symfony Command中自定义脚本把Excel数据导入到数据库中

    // 注:只是在此做下记录,有兴趣的可以参考,不做实际教程文档 <?php/** * Created by IntelliJ IDEA. * User: davis * Date: 2019-0 ...

  3. 170313、poi:采用自定义注解的方式导入、导出excel(这种方式比较好扩展)

    步骤一.自定义注解 步骤二.写Excel泛型工具类 步骤三.在需要导出excel的类属相上加上自定义注解,并设置 步骤四.写service,controller 步骤一:自定义注解 import ja ...

  4. Excel导入sq server后数据列以科学计数法显示

    一.选中excel数据列如图 二.选择数据--分列 三.选择下一步,下一步,文本 四.完成 五.这样把excel导入到数据库中是以文本形式显示不会出现科学计数法

  5. 【Java编程思想笔记】注解--自定义注解

    文章参考自:https://www.cnblogs.com/xdp-gacl/p/3622275.html 学习网站:how2java.cn 一.自定义注解的创建过程 第一步:(元注解)   使用元注 ...

  6. Java实现自定义注解开发

    Java实现自定义注解开发 一直都对注解开发挺好奇的,最近终于有时间自己实践了一把,记录一下 万一后期会用到呢 哈哈哈 首先我们了解一下自定义注解的标准示例,注解类使用 @interface 关键字修 ...

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

    MySQL数据列类型选择是在我们设计表的时候经常会遇到的问题,下面就教您如何正确选择MySQL数据列类型,供您参考学习. 选择正确的数据列类型能大大提高数据库的性能和使数据库具有高扩展性.在选择MyS ...

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

    这次给大家介绍一下在Java开发过程中 使用自定义注解开发:主要知识点:            1.反射            主要用于提取注解信息            2.自定义异常  主要是为了 ...

  9. Java利用自定义注解、反射实现简单BaseDao

    在常见的ORM框架中,大都提供了使用注解方式来实现entity与数据库的映射,这里简单地使用自定义注解与反射来生成可执行的sql语句. 这是整体的目录结构,本来是为复习注解建立的项目^.^ 好的,首先 ...

随机推荐

  1. IDEA下使用maven构建web项目(SpringMVC+Mybatis整合)

    需求背景:由于最近总是接到一些需求,需要配合前端团队快速建设移动端UI应用或web应用及后台业务逻辑支撑的需求,若每次都复用之前复杂业务应用的项目代码,总会携带很多暂时不会用到的功能或组件,这样的初始 ...

  2. AngularJs 常用的过滤器

    date格式化 {{ 1304375948024 | date }}             //结果:May 3, 2011 {{ 1304375948024 | date:"MM/dd/ ...

  3. 华硕 F1A55-M LX3系列跳线图

    天啊,第一次遇到这么变态的主板跳线...浪费我好久时间找到这跳线图

  4. PS不能存储,因为程序错误

    当PS中遇到不能存储文件,因为程序错误时,可以这样: http://www.zcool.com.cn/article/ZMTgwOTQw.html

  5. 最牛分布式消息系统:Kafka

    Kafka是分布式发布-订阅消息系统.它最初由LinkedIn公司开发,之后成为Apache项目的一部分.Kafka是一个分布式的,可划分的,冗余备份的持久性的日志服务.它主要用于处理活跃的流式数据. ...

  6. HTTP 和 HTTPS

    一.HTTP协议 最近看了一些网络通信方面的书籍,研究了一下 HTTP 和 TCP/IP,有了一些新的收获和理解,在这里做个归纳和总结. (1)什么是HTTP协议 HTTP (HyperText Tr ...

  7. 2017 UESTC Training for Graph Theory

    图论姿势太弱,这套题做了好久.. A:枚举最短那条边,然后最小生成树那种操作,1 和 n 联通就算答案 B:考虑到假如我们能凑出x的话,那很明显我们也能凑出任意数表示x + ai,考虑选取一个ai,然 ...

  8. .Net程序员学用Oracle系列(8):触发器、作业、序列、连接

    1.触发器 2.作业 2.1.作业调度功能和应用 2.2.通过 DBMS_JOB 来调度作业 3.序列 3.1.创建序列 3.2.使用序列 & 删除序列 4.连接 4.1.创建连接 4.2.使 ...

  9. 项目中的报错信息,maven报错等的总结

    Maven是一个自动化的构建和管理工具.在项目开发中,如果遇到了错误(红叉),一般有如下的解决方法: 1.java.lang.UnsatisfiedLinkError: E:\apache-tomca ...

  10. Chart.js – 效果精美的 HTML5 Canvas 图表库

    Chart.js 是一个令人印象深刻的 JavaScript 图表库,建立在 HTML5 Canvas 基础上.目前,它支持6种图表类型(折线图,条形图,雷达图,饼图,柱状图和极地区域区).而且,这是 ...