最近老师布置了一个任务,实现 Excel 和数据库的导入导出。

这个问题起初看起来是不复杂,实现简陋的导入导出比较容易。

但是,后来老师要求写一个包装类,让其他同学能够直接使用。

这就涉及到了泛型和反射的内容,其实也不复杂。

最终,在网上找到一段代码,写得挺好的,然后一顿调试和修改,就能直接拿来用了。

现在也分享给大家。

 

基本功能

* 1.实体属性配置了注解就能导出到excel中,每个属性都对应一列.

* 2.列名称可以通过注解配置.

* 3.导出到哪一列可以通过注解配置.

* 4.鼠标移动到该列时提示信息可以通过注解配置.

* 5.用注解设置只能下拉选择不能随意填写功能.

* 6.用注解设置是否只导出标题而不导出内容,这在导出内容作为模板以供用户填写时比较实用.

 

效果图

数据库

 

Excel

 

调用方法

下面是通过 SpringMVC 操作,导入导出

1、导出



1. /**
2.     * 将user表导出到Excel表
3.     * @return
4.     */
5. @RequestMapping(value = "/exportExcel")
6. @ResponseBody
7. public
8. // 初始化数据
9.        List<User> userList = userService.listUser();
10. 
11. new
12. 
13. //将 userList 拷贝到 userVOList 中
14. for(int i=0; i<userList.size(); i++) {

15. new
16.            User user = userList.get(i);
17.            BeanUtils.copyProperties(user, userVO);
18.            userVOList.add(userVO);
19.        }
20. 
21. //将 userVoList 写入到数据库中
22. null;
23. try
24. new FileOutputStream("/Users/liuyanzhao/Desktop/Test/demo.xls");
25. catch
26.            e.printStackTrace();
27.        }
28. new ExcelUtil<UserVO>(UserVO.class);// 创建工具类.
29. "用户信息", 65536, out);// 导出
30. "----执行完毕----------");
31. return "success";
32.    }

 

2、导入



1. @RequestMapping(value = "/importExcel")
2. @ResponseBody
3. public
4. null;
5. try
6. new FileInputStream("/Users/liuyanzhao/Desktop/Test/demo2.xls");
7. new
8. class);
9. // 创建excel工具类,返回Excel中的数据
10. "用户信息", fis);// 导入
11.             System.out.println(userVOList);
12. //将userVOList 转成 userList
13. new
14. for(int i = 0; i < userVOList.size(); i++) {

15. new
16.                 UserVO userVO = userVOList.get(i);
17.                 BeanUtils.copyProperties(userVO,user);
18.                 userList.add(user);
19.             }
20. //插入数据库中
21.             userService.insertUserBatch(userList);
22. catch
23.             e.printStackTrace();
24.         }
25. return "success";
26.     }

 

封装实现

1、ExcelVOAttribute.java  自定义注解



1. package
2. 
3. import
4. import
5. import
6. 
7. /**
8.  * @author 言曌
9.  * @date 2017/12/25 上午10:19
10.  */
11. 
12. @Retention(RetentionPolicy.RUNTIME)
13. @Target( { java.lang.annotation.ElementType.FIELD })
14. public @interface
15. 
16. /**
17.      * 导出到Excel中的名字.
18.      */
19. public abstract
20. 
21. /**
22.      * 配置列的名称,对应A,B,C,D....
23.      */
24. public abstract
25. 
26. /**
27.      * 提示信息
28.      */
29. public abstract String prompt() default "";
30. 
31. /**
32.      * 设置只能选择不能输入的列内容.
33.      */
34. public abstract String[] combo() default
35. 
36. /**
37.      * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
38.      */
39. public abstract boolean isExport() default true;
40. 
41. }

 

2、ExcelUtil.java   Excel操作封装类



1. package
2. 
3. import
4. import
5. import
6. import
7. import
8. import
9. import
10. import
11. import
12. import
13. 
14. import
15. import
16. 
17. /**
18.  * @author 言曌
19.  * @date 2017/12/24 下午9:08
20.  */
21. 
22. /*
23.  * ExcelUtil工具类实现功能:
24.  * 导出时传入list<T>,即可实现导出为一个excel,其中每个对象T为Excel中的一条记录.
25.  * 导入时读取excel,得到的结果是一个list<T>.T是自己定义的对象.
26.  * 需要导出的实体对象只需简单配置注解就能实现灵活导出,通过注解您可以方便实现下面功能:
27.  * 1.实体属性配置了注解就能导出到excel中,每个属性都对应一列.
28.  * 2.列名称可以通过注解配置.
29.  * 3.导出到哪一列可以通过注解配置.
30.  * 4.鼠标移动到该列时提示信息可以通过注解配置.
31.  * 5.用注解设置只能下拉选择不能随意填写功能.
32.  * 6.用注解设置是否只导出标题而不导出内容,这在导出内容作为模板以供用户填写时比较实用.
33.  * 本工具类以后可能还会加功能,请关注我的博客: 
34.  */
35. public class
36.     Class<T> clazz;
37. 
38. public
39. this.clazz = clazz;
40.     }
41. 
42. public
43. new
44. try
45. new
46.             HSSFSheet sheet = workbook.getSheet(sheetName);
47. if (!sheetName.trim().equals("")) {

48. // 如果指定sheet名,则取指定sheet中的内容.
49.             }
50. if (sheet == null) {

51. 0); // 如果传入的sheet名不存在则默认指向第1个sheet.
52.             }
53. int
54. 
55. if (rows > 0) {// 有数据时才处理
56. // 得到类的所有field.
57. new HashMap<Integer, Field>();// 定义一个map用于存放列的序号和field.
58. for
59. // 将有注解的field存放到map中.
60. if (field.isAnnotationPresent(ExcelVOAttribute.class)) {

61.                         ExcelVOAttribute attr = field
62. class);
63. int col = getExcelCol(attr.column());// 获得列号
64. // System.out.println(col + "====" + field.getName());
65. true);// 设置类的私有字段属性可访问.
66.                         fieldsMap.put(col, field);
67.                     }
68.                 }
69. for (int i = 1; i < rows; i++) {// 从第2行开始取数据,默认第一行是表头.
70.                     HSSFRow row = sheet.getRow(i);
71. int
72. null;
73. for (int j = 0; j < cellNum; j++) {

74.                         HSSFCell cell = row.getCell(j);
75. if (cell == null) {

76. continue;
77.                         }
78. "";
79. switch
80. case HSSFCell.CELL_TYPE_NUMERIC: // 数字
81. //如果为时间格式的内容
82. if
83. //注:format格式 yyyy-MM-dd hh:mm:ss 中小时为12小时制,若要24小时制,则把小h变为H即可,yyyy-MM-dd HH:mm:ss
84. new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
85.                                     value = sdf.format(HSSFDateUtil.getJavaDate(cell.
86.                                             getNumericCellValue())).toString();
87. break;
88. else
89. new DecimalFormat("0").format(cell.getNumericCellValue());
90.                                 }
91. break;
92. case HSSFCell.CELL_TYPE_STRING: // 字符串
93.                                 value = cell.getStringCellValue();
94. break;
95. case HSSFCell.CELL_TYPE_BOOLEAN: // Boolean
96. "";
97. break;
98. case HSSFCell.CELL_TYPE_FORMULA: // 公式
99. "";
100. break;
101. case HSSFCell.CELL_TYPE_BLANK: // 空值
102. "";
103. break;
104. case HSSFCell.CELL_TYPE_ERROR: // 故障
105. "非法字符";
106. break;
107. default:
108. "未知类型";
109. break;
110.                         }
111.                         System.out.println(value);
112. if (value.equals("")) {

113. continue;
114.                         }
115. null ? clazz.newInstance() : entity);// 如果不存在实例则新建.
116. // System.out.println(cells[j].getContents());
117. // 从map中得到对应列的field.
118. // 取得类型,并根据对象类型设置值.
119.                         Class<?> fieldType = field.getType();
120. if (String.class
121.                             field.set(entity, String.valueOf(value));
122. else if
123. class
124.                             field.set(entity, Integer.parseInt(value));
125. else if
126. class
127.                             field.set(entity, Long.valueOf(value));
128. else if
129. class
130.                             field.set(entity, Float.valueOf(value));
131. else if
132. class
133.                             field.set(entity, Short.valueOf(value));
134. else if
135. class
136.                             field.set(entity, Double.valueOf(value));
137. else if
138. if ((value != null) && (value.length() > 0)) {

139.                                 field.set(entity, Character
140. 0)));
141.                             }
142.                         }
143. 
144.                     }
145. if (entity != null) {

146.                         list.add(entity);
147.                     }
148.                 }
149. 
150.             }
151. 
152. catch
153.             e.printStackTrace();
154. catch
155.             e.printStackTrace();
156. catch
157.             e.printStackTrace();
158. catch
159.             e.printStackTrace();
160.         }
161. return
162.     }
163. 
164. /**
165.      * 对list数据源将其里面的数据导入到excel表单
166.      *
167.      * @param sheetName
168.      *            工作表的名称
169.      * @param sheetSize
170.      *            每个sheet中数据的行数,此数值必须小于65536
171.      * @param output
172.      *            java输出流
173.      */
174. public boolean exportExcel(List<T> list, String sheetName, int
175.                                OutputStream output) {

176. 
177. // 得到所有定义字段
178. new
179. // 得到所有field并存放到一个list中.
180. for
181. if (field.isAnnotationPresent(ExcelVOAttribute.class)) {

182.                 fields.add(field);
183.             }
184.         }
185. 
186. new HSSFWorkbook();// 产生工作薄对象
187. 
188. // excel2003中每个sheet中最多有65536行,为避免产生错误所以加这个逻辑.
189. if (sheetSize > 65536 || sheetSize < 1) {

190. 65536;
191.         }
192. double sheetNo = Math.ceil(list.size() / sheetSize);// 取出一共有多少个sheet.
193. for (int index = 0; index <= sheetNo; index++) {

194. // 产生工作表对象
195. if (sheetNo == 0) {

196.                 workbook.setSheetName(index, sheetName);
197. else
198. // 设置工作表的名称.
199.             }
200.             HSSFRow row;
201. // 产生单元格
202. 
203. 0);// 产生一行
204. // 写入各个字段的列头名称
205. for (int i = 0; i < fields.size(); i++) {

206.                 Field field = fields.get(i);
207.                 ExcelVOAttribute attr = field
208. class);
209. int col = getExcelCol(attr.column());// 获得列号
210. // 创建列
211. // 设置列中写入内容为String类型
212. // 写入列名
213. 
214. // 如果设置了提示信息则鼠标放上去提示.
215. if (!attr.prompt().trim().equals("")) {

216. "", attr.prompt(), 1, 100, col, col);// 这里默认设了2-101列提示.
217.                 }
218. // 如果设置了combo属性则本列只能选择不能输入
219. if (attr.combo().length > 0) {

220. 1, 100, col, col);// 这里默认设了2-101列只能选择不能输入.
221.                 }
222.             }
223. 
224. int
225. int
226. // 写入各条记录,每条记录对应excel表中的一行
227. for (int
228. 1
229. // 得到导出对象.
230. for (int j = 0; j < fields.size(); j++) {

231. // 获得field.
232. true);// 设置实体类私有属性可访问
233.                     ExcelVOAttribute attr = field
234. class);
235. try
236. // 根据ExcelVOAttribute中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
237. if
238. // 创建cell
239.                             cell.setCellType(HSSFCell.CELL_TYPE_STRING);
240. null ? ""
241. // 如果数据存在就填入,不存在填入空格.
242.                         }
243. catch
244.                         e.printStackTrace();
245. catch
246.                         e.printStackTrace();
247.                     }
248.                 }
249.             }
250. 
251.         }
252. try
253.             output.flush();
254.             workbook.write(output);
255.             output.close();
256. return true;
257. catch
258.             e.printStackTrace();
259. "Output is closed ");
260. return false;
261.         }
262. 
263.     }
264. 
265. /**
266.      * 将EXCEL中A,B,C,D,E列映射成0,1,2,3
267.      *
268.      * @param col
269.      */
270. public static int
271.         col = col.toUpperCase();
272. // 从-1开始计算,字母重1开始运算。这种总数下来算数正好相同。
273. int count = -1;
274. char[] cs = col.toCharArray();
275. for (int i = 0; i < cs.length; i++) {

276. 64) * Math.pow(26, cs.length - 1
277.         }
278. return
279.     }
280. 
281. /**
282.      * 设置单元格上提示
283.      *
284.      * @param sheet
285.      *            要设置的sheet.
286.      * @param promptTitle
287.      *            标题
288.      * @param promptContent
289.      *            内容
290.      * @param firstRow
291.      *            开始行
292.      * @param endRow
293.      *            结束行
294.      * @param firstCol
295.      *            开始列
296.      * @param endCol
297.      *            结束列
298.      * @return 设置好的sheet.
299.      */
300. public static
301. int firstRow, int endRow, int
302. int
303. // 构造constraint对象
304.         DVConstraint constraint = DVConstraint
305. "DD1");
306. // 四个参数分别是:起始行、终止行、起始列、终止列
307. new
308.                 endRow, firstCol, endCol);
309. // 数据有效性对象
310. new
311.                 regions, constraint);
312.         data_validation_view.createPromptBox(promptTitle, promptContent);
313.         sheet.addValidationData(data_validation_view);
314. return
315.     }
316. 
317. /**
318.      * 设置某些列的值只能输入预制的数据,显示下拉框.
319.      *
320.      * @param sheet
321.      *            要设置的sheet.
322.      * @param textlist
323.      *            下拉框显示的内容
324.      * @param firstRow
325.      *            开始行
326.      * @param endRow
327.      *            结束行
328.      * @param firstCol
329.      *            开始列
330.      * @param endCol
331.      *            结束列
332.      * @return 设置好的sheet.
333.      */
334. public static
335. int firstRow, int endRow, int
336. int
337. // 加载下拉列表内容
338.         DVConstraint constraint = DVConstraint
339.                 .createExplicitListConstraint(textlist);
340. // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
341. new
342.                 endRow, firstCol, endCol);
343. // 数据有效性对象
344. new
345.                 regions, constraint);
346.         sheet.addValidationData(data_validation_list);
347. return
348.     }
349. }