【Java】用 poi-3.15.jar 实现对excel表格的读和写 (尝试完成从excel内取出对象list和存入某对象至excel的工具)

因为舍友项目的某种需求,需要将数据存储在excel表格中,我就下载并尝试使用了 poi-3.15 ,下面是我自己在学习中的一点所得

poi-3.15 下载地址:https://mvnrepository.com/artifact/org.apache.poi/poi/3.15

这是一个简单的excel表格,第一行给出了 表头

实际上poi包的基本操作也是对  cell 的操作

 

目录

建立

写入

自定义注解

从表中获取对象list

将对象存入Excel表测试:



建立

来看一个建立表的例子

//创建一个hssfWorkbook与一个excel对应
HSSFWorkbook hssfWorkbook = new HSSFWorkbook();
// 创建对应的sheet
HSSFSheet hssfSheet = hssfWorkbook.createSheet("message");
// 添加表格第0行
HSSFRow row = hssfSheet.createRow(0);
// 设置单元格,设置表头
HSSFCell cell = row.createCell(0);
cell.setCellValue("id");
cell = row.createCell(1);
cell.setCellValue("password");

这样就建立好一个表了,hssfSheet就是这个excel表格的一个“面”,叫“message”

通过这个面hssfSheet可以新建一个给定行号的行row

通过行可以构建一个给定列号的单元格cell

即,cell是某面某行某列的单元格

所有的操作都是基于单元格来说的

切记!保存操作:

FileOutputStream fos;
try {
    // 输出流,新建excel表(路径文件名)
	fos = new FileOutputStream("message.xls"); 
    // 将hssfWorkbook对应的excel表格写入
	hssfWorkbook.write(fos);
    // 关闭输出流
	fos.close();
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
}

结果:


写入

// 写 的前提是有这个excel表,因此要【取】也要【存】
// 故文件输入流和输出流都必须有
FileInputStream fis = null;
FileOutputStream fos = null;
try {
    // 找到文件
	fis = new FileInputStream("message.xls");
	HSSFWorkbook hssfWorkbook = new HSSFWorkbook(fis);
	HSSFSheet hssfSheet = hssfWorkbook.getSheet("message");
	if (hssfSheet == null) {
		// 未找到的操作
	}
    // 定位最后一行位置并新建行
	int saveIndex = hssfSheet.getLastRowNum() + 1;
	HSSFRow row = hssfSheet.createRow(saveIndex);

    // 创建cell并填入
    row.createCell(0).setCellValue("003");
    row.createCell(1).setCellValue("003300");
    
    // 写入文件
    fos = new FileOutputStream("message.xls");
    hssfWorkbook.write(fos);

    } catch (FileNotFoundException e) {
	    e.printStackTrace();
    } catch (IOException e) {
    	e.printStackTrace();
    } catch (IllegalArgumentException e) {
	    e.printStackTrace();
    } finally {
	    fis.close();
	    fos.close();
    }

结果:


FileInputStream fis = null;
try {
	fis = new FileInputStream("message.xls");
} catch (FileNotFoundException e) {
	e.printStackTrace();
}
HSSFWorkbook hssfWorkbook;
try {
	hssfWorkbook = new HSSFWorkbook(fis);
	HSSFSheet hssfSheet = hssfWorkbook.getSheet("message");
	HSSFRow row = hssfSheet.getRow(1);
	System.out.println(row.getCell(0));
	System.out.println(row.getCell(1));	
} catch (IOException e) {
	e.printStackTrace();
}

运行结果:


这就是poi最简单的用法了,思考这样一个问题:实际编程中我们需要将一个对象写入excel表,或将excel表中的每条数据(每一行)读出来形成一个list,该如何完成?反射机制!

自己尝试的做了做,虽然只是局限于“竖”形表且只有一个“面”,但也好歹完成了读取list和写入对象的操作

 

问题:

1.如何处理HssfSheet名字的问题?这里我用类名字作为其HssfSheet名

2.如何处理类成员和表头的对应关系?采用注解方式

自定义注解


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Cell {
	String value();
}

使用该注解时必须注明其对应excel表的哪一列 

从表中获取对象list

public static <T> List<T> getList(String excelPath, Class<?> klass) throws Exception {
    List<T> resultList = new ArrayList<>();
    FileInputStream fis = null;
    try {
        // 由参数传来的文件路径获取输入流,得到“面”sheet,得到表头
        fis = new FileInputStream(excelPath);
        HSSFWorkbook hssfWorkbook = new HSSFWorkbook(fis);
        HSSFSheet hssfSheet = hssfWorkbook.getSheet(klass.getSimpleName());
        HSSFRow headRow = hssfSheet.getRow(0);
			
        // 检查传来的类的成员数是否等于表的列数
        int index = 0;
        HSSFCell cell = null;
        Field[] fields = klass.getDeclaredFields();
        do {
            cell = headRow.getCell(index++);
        } while (cell != null);

        if (fields.length != --index) {
            throw new Exception(excelPath + "列数与类" + klass + "成员数不匹配");
        }
		
        // 从第一行开始遍历
        // 每遍历一行,新建一个该类的对象
        index = 1;
        HSSFRow row = null;
        do {
            row = hssfSheet.getRow(index++);
            if (row == null) {
                break;
            }
            int colIndex = 0;
            Object object = klass.newInstance();
				
            // 遍历该行的每一个cell,找到与之对应的成员(带有cell注解,且value为列名字)
            // 将其设置到对象成员中
            do {
                cell = row.getCell(colIndex);
                if (cell == null) {
                    break;
                }				
                for (int i = 0; i < fields.length; i++) {
                    Field field = fields[i];
                    Cell fieldCell = field.getAnnotation(Cell.class);
						
                    if (headRow.getCell(colIndex).getStringCellValue()
                       .equals(fieldCell.value())) {
                            // 设置成员方法
                            setField(cell, object, field);
                        }
                }
                resultList.add((T) object);
				
            } while (row != null);
			
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            fis.close();
        }
        return resultList;
    }

setField方法:

将表内单元格数据赋给队象的成员并没有那么简单,这里涉及类型转化

个人思想:先将单元格内数据转化为String类型,再在赋值时根据成员类型将数据转化为相应类型,完成赋值!当然,这里只支持String + 八大基本类型

private static void setField(HSSFCell cell, Object object, Field field) throws Exception {
    Class<?> fieldType = field.getType();
    cell.setCellType(CellType.STRING);
    field.setAccessible(true);
    try {
    if (fieldType.equals(String.class)) {
        field.set(object,cell.getStringCellValue());		
    } else if (fieldType.equals(int.class)) {
        field.set(object,Integer.parseInt(cell.getStringCellValue()));
    } else if (fieldType.equals(double.class)) {
        field.set(object,Double.parseDouble(cell.getStringCellValue()));
    } else if (fieldType.equals(float.class)) {
        field.set(object,Float.parseFloat(cell.getStringCellValue()));
    } else if (fieldType.equals(boolean.class)) {
        field.set(object,Boolean.parseBoolean(cell.getStringCellValue()));
    } else if (fieldType.equals(byte.class)) {
        field.set(object,Byte.parseByte(cell.getStringCellValue()));
    } else if (fieldType.equals(long.class)) {
        field.set(object,Long.parseLong(cell.getStringCellValue()));
    } else if (fieldType.equals(short.class)) {
        field.set(object,Short.parseShort(cell.getStringCellValue()));
    } else if (fieldType.equals(char.class)) {
        field.set(object,cell.getStringCellValue().charAt(0));
    } else {
        throw new Exception("不支持的类型");
        }
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

将对象存入Excel表

给定Excel表路径,将对象存至该表,若未找到文件,则以类名字为excel表名字新建文件

private static volatile boolean newExcel = false;

public static synchronized void creatExcel(Object object) throws Exception {
    save("", object);
}

public static synchronized void save(String excelPath, Object object) throws Exception {
    FileInputStream fis = null;
    FileOutputStream fos = null;
    HSSFWorkbook hssfWorkbook = null;
    HSSFSheet hssfSheet = null;
    HSSFRow headRow = null;
    Class<?> klass = object.getClass();
    try {
        try {
            fis = new FileInputStream(excelPath);

            // 这里借助了异常,如果找不到该文件,则新建excel表
        } catch (FileNotFoundException e) {
            hssfWorkbook = new HSSFWorkbook();
            hssfSheet = hssfWorkbook.createSheet(klass.getSimpleName());
            headRow = hssfSheet.createRow(0);
			
            // 新建表时要设计表头,利用注解						
            setExcelHead(klass, headRow);
        } finally {	
            // 保存部分,若表不是新建的则获取旧表数据			
            if (!newExcel) {
                hssfWorkbook = new HSSFWorkbook(fis); 
                hssfSheet = hssfWorkbook.getSheet(klass.getSimpleName());
                headRow = hssfSheet.getRow(0);
            }			
			
            // 	保存方法
            doSave(hssfSheet, object);
            fos = new FileOutputStream(excelPath);
            hssfWorkbook.write(fos);
            }
        } catch (IOException e) {
			e.printStackTrace();
        } catch (IllegalArgumentException e) {
			e.printStackTrace();
        } finally {
            newExcel = false;
            if (fis != null) {
                fis.close();
            }
            if (fos != null) {
                fos.close();
            }
        }
}

setExcelHead 设置表头

private static void setExcelHead(Class<?> klass, HSSFRow headRow) throws Exception {
    Field[] headFields = klass.getDeclaredFields();
    for (int i = 0; i < headFields.length; i++) {
        Field headField = headFields[i];
        if (!headField.isAnnotationPresent(Cell.class)) {
            throw new Exception(headField + "缺少注解!");
        }
        Cell cell = headField.getAnnotation(Cell.class);
        HSSFCell hssfCell = headRow.createCell(i);
        hssfCell.setCellValue(cell.value());
    }					
    newExcel = true;
}

遍历对象的所有成员,取其注解的value,设置表头

doSave 将对象保存至表中

private static void doSave(HSSFSheet hssfSheet, Object object) {
    // 定位最后一行,创建新行
    int saveIndex = hssfSheet.getLastRowNum() + 1;
    HSSFRow row = hssfSheet.createRow(saveIndex);
    Field[] fields = object.getClass().getDeclaredFields();
    // 获取对象的所有成员,准备填入表
    for (int i = 0; i < fields.length; i++) {
        Field field = fields[i];
		
        Cell myCell = field.getAnnotation(Cell.class);
        HSSFRow headerRow = hssfSheet.getRow(0);
        int lastCellNum = headerRow.getLastCellNum();
        for (int col = 0; col < lastCellNum; col++) {
            if (myCell.value().equals(headerRow.getCell(col).getStringCellValue())) {
                // 若单元格列名与注解value相同 设置单元格		
                setCell(object, field, row.createCell(col));
            }
        }
    }
}

setCell 设置单元格

private static void setCell(Object object, Field field, HSSFCell cell) {
    field.setAccessible(true);
    Class<?> fieldType = field.getType();
    try {
        if (fieldType.equals(String.class)) {
            cell.setCellValue(field.get(object).toString());
        } else if (fieldType.equals(int.class)) {
            cell.setCellValue(field.getInt(object));
        } else if (fieldType.equals(double.class)) {
            cell.setCellValue(field.getDouble(object));
        } else if (fieldType.equals(float.class)) {
            cell.setCellValue(field.getFloat(object));
        } else if (fieldType.equals(short.class)) {
            cell.setCellValue(field.getShort(object));
        } else if (fieldType.equals(long.class)) {
            cell.setCellValue(field.getLong(object));
        } else if (fieldType.equals(char.class)) {
            cell.setCellValue(field.getChar(object));
        } else if (fieldType.equals(byte.class)) {
            cell.setCellValue(field.getByte(object));
        } else if (fieldType.equals(boolean.class)) {
            cell.setCellValue(field.getBoolean(object));
        }
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

这里要注意的在于field的类型转换,根据field的类型来获取其值


测试:

public class Message {
	@Cell("id")
	private int id;
	@Cell("password")
	private String password;
	
	public Message() {
	}
	
	public Message(int id, String password) {
		this.id = id;
		this.password = password;
	}

    // 各成员的 get 和 set 方法 省略

        @Override
	public String toString() {
		return "Message [id=" + id + ", password=" + password + "]";
	}
}
public class Test {
	
	public static void main(String[] args) {
		try {	
			Message message = new Message(54188, "funyoo");
			ExcelHandler.save("message.xls", message);
			
			List<Message> list = ExcelHandler.getList("message.xls", Message.class);
						
			for (int i = 0; i < list.size(); i++) {
				System.out.println(list.get(i));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

测试结果: 

 总结:

这样就尝试着完成了一个很low的从excel表中获取对象list和存入对象

关于成员和表头匹配还是略显臃肿,应该有更妙的解决方案

为保证安全,加了锁

还有太多方面需要完善,这里只是“竖”形表,另外,自己还没完成指定查询和删除,删除肯定还需要大量数据在表内的移动

 

望,各位多提意见

 

全部评论

相关推荐

11-03 14:38
重庆大学 Java
AAA求offer教程:我手都抬起来了又揣裤兜了
点赞 评论 收藏
分享
object3:开始给部分🌸孝子上人生第一课了
点赞 评论 收藏
分享
点赞 1 评论
分享
牛客网
牛客企业服务