1.ET简介
在大型企业系统中,有一个必不可少的业务,就是--报表。为了阅读、处理方便,报表通常都是以excel的文件形式进行存储的。所幸的是已经有大佬开发了一个用于处理excel的框架--jxl。既然有了jxl,为什么还要开发ET?第一,jxl虽然已经把api封装的很好了,但是对于业务层还是不太友好的,我要实现一个业务,需要很多步骤的api调用,故此,写一个更高层次的封装,更贴近业务;第二,本人太闲了,顺便当练练手,虽然写的很差= =。
源码地址:https://github.com/a1104321118/ET
2.ET设计思想
打开excel,发现它和什么很像?没错,数据库啊。所以说ET的设计思想就是来源于数据库,虽然它很简单。首先,数据库的表有很多列,每一个列都有列名。所以我们可以把excel也看成很多列,把第一行的都设置成类名。进一步,数据库的表一般都对应着实体类。所以我们可以把excel也对应成实体类。这不正是Mybatis的设计思想吗?
3.ET-demo
首先,看一个excel,这是我们的原始数据,第一行就是“列名”,下面的几行是数据:
再看一个实体类:
public class Person {
private String name;
private String gender;
private String age;
private String state;
//getter/setter
....
}
其中,excel的列名和Person实体类的属性名是映射关系。
接下来看ET的使用方法:
public class Main {
public static void main(String[] args) {
//文件输入输出路径
String filePath = "D:\\idea\\MyProjects\\ET\\src\\main\\java\\resources\\test.xls";
String outFilePath = "D:\\idea\\MyProjects\\ET\\src\\main\\java\\resources\\test-result.xls";
ExcelTool excelTool = new ExcelTool(filePath);
//读数据
List<Person> peopleList = excelTool.readData(Person.class);
//修改数据
for (Person person : peopleList){
System.out.println(person);
person.setState("SUCCESS");
}
//写数据
excelTool.writeData(peopleList, outFilePath);
}
}
运行结果:
至此,可以看出ET有多简单了把。
4.ET-源码解析
其实ET的源码非常简单,最核心的思想,就是把excel的列名,和Person类的属性名进行映射,并实例出person对象。怎么做?反射。直接贴出源码,注释非常详细:
package com.hr.et.tool;
import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;
import jxl.write.Label;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
* Created by hr on 2017/08/03.
*
*/
public class ExcelTool {
//读取文件
private Workbook workbook = null;
//写文件
private WritableWorkbook writableWorkbook = null;
/**
* 初始化工具,需要传入文件路径
* @param filePath
* @throws IOException
* @throws BiffException
*/
public ExcelTool(String filePath){
File file = new File(filePath);
if(!file.isFile() || !file.exists()){
try {
throw new FileNotFoundException("文件不存在");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
try {
this.workbook = Workbook.getWorkbook(file);
} catch (IOException e) {
e.printStackTrace();
} catch (BiffException e) {
e.printStackTrace();
}
}
/**
* 读取全部数据
* @param clazz
* @param <T>
* @return
* @throws IllegalAccessException
* @throws InstantiationException
*/
public <T> List<T> readData(Class<T> clazz){
try {
return readData(clazz, 1, 0);
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
return null;
}
/**
* 读取数据,从 begin 开始,数量为num
* @param clazz
* @param begin
* @param num
* @param <T>
* @return
* @throws IllegalAccessException
* @throws InstantiationException
*/
public <T> List<T> readData(Class<T> clazz, int begin, int num) throws IllegalAccessException, InstantiationException {
List<T> result = new ArrayList<>();
Sheet sheet = this.workbook.getSheet(0);
//获得所有列名
List<String> cloumnNames = new ArrayList<>();
int cloumns = sheet.getColumns();
for (int i=0; i<cloumns; i++){
cloumnNames.add(sheet.getCell(i, 0).getContents());
}
//获得该类的属性
Field[] fields = clazz.getDeclaredFields();
int rows = sheet.getRows();
begin = begin <= 0 ? begin+1 : begin;
int end = (begin + num) > rows ? rows : (begin + num);
if(num == 0){
end = rows;
}
for (int i=begin; i<end; i++){//对每一行数据进行处理
Cell[] row = sheet.getRow(i);//获得这一行的数据
T t = clazz.newInstance(); //通过反射实例化泛型对象
for (int j=0; j<row.length; j++){//对这一行的数据进行一个一个处理
String thisCloumnName = cloumnNames.get(j);//先获得列名
for (Field field : fields){
if(thisCloumnName.equals(field.getName())){//当列名和这个属性的名称一样时,认为他们相互映射
field.setAccessible(true);
field.set(t, row[j].getContents());//设置属性值
}
}
}
result.add(t);
}
workbook.close();
return result;
}
/**
* 写数据
* @param data
* @param <T>
*/
public <T> void writeData(List<T> data, String outFilePath) {
//判断数据有效性
if(null == data || data.size() == 0){
return;
}
if(null == outFilePath || outFilePath.equals("")){
return;
}
WritableSheet sheet = null;
File file = new File(outFilePath);
try {
this.writableWorkbook = Workbook.createWorkbook(file);
} catch (IOException e) {
e.printStackTrace();
}
sheet = this.writableWorkbook.createSheet("result", 0);
//写入标题,顺序是fields的顺序
T t = data.get(0);
Field[] fields = t.getClass().getDeclaredFields();
Label[] head = new Label[fields.length];
for (int i=0; i<fields.length; i++){
head[i] = new Label(i, 0, fields[i].getName());
}
writeLine(sheet, head);//先把Person的属性都写入excel的第一行
//写入数据
try {
for (int i = 0; i < data.size(); i++) {//开始写list里面的数据
T result = data.get(i);
for (int j = 0; j < fields.length; j++) {//逐个写属性
Field field = result.getClass().getDeclaredField(fields[j].getName());
field.setAccessible(true);
String s = (String)field.get(result);
sheet.addCell(new Label(j, i+1, s));
}
}
}catch (NoSuchFieldException | WriteException | IllegalAccessException e) {
e.printStackTrace();
}
try {
this.writableWorkbook.write();
this.writableWorkbook.close();
} catch (WriteException | IOException e) {
e.printStackTrace();
}
}
private void writeLine(WritableSheet sheet, Label[] data){
try {
for (int i = 0; i < data.length; i++) {
sheet.addCell(data[i]);
}
} catch (WriteException e) {
e.printStackTrace();
}
}
}
main方法
public static void main(String[] args) {
//文件输入输出路径
String filePath = "D:\\idea\\MyProjects\\ET\\src\\main\\java\\resources\\test.xls";
String outFilePath = "D:\\idea\\MyProjects\\ET\\src\\main\\java\\resources\\test-result.xls";
ExcelTool excelTool = new ExcelTool(filePath);
//读数据
List<Person> peopleList = excelTool.readData(Person.class);
//修改数据
for (Person person : peopleList){
System.out.println(person);
person.setState("SUCCESS");
}
//写数据
excelTool.writeData(peopleList, outFilePath);
}
5.注意事项
5.1不要把读取的文件和写入的文件搞成同一个文件!!!
不要把读取的文件和写入的文件搞成同一个文件!!!
不要把读取的文件和写入的文件搞成同一个文件!!!
否则报错时,原数据会被覆盖,造成数据丢失。
5.2目前只有String类型
确实,这个东西只写了一下午,还有很多东西没有完善,但是一般的功能业务足够了。以后有空再研究一下int,date这些东西能不能映射成功。
6.其它
转载请附上原文地址:https://my.oschina.net/u/3582320/blog/1501527
源码地址:https://github.com/a1104321118/ET
欢迎大家留言讨论。