实验采用Mybatis+POI+JSON 完成解析Excel数据,并将数据批量插入数据库。
1.POI+JSON 提取Excel中数据,同时映射实体类。
/**
*
* @param is 输入流
* @return 输入流创建XSSFWorkBook对象
*/
public XSSFWorkbook XSSFReadFile(InputStream is) {
XSSFWorkbook wb=null;
try {
wb=new XSSFWorkbook(is);
} catch (IOException e) {
e.printStackTrace();
}
return wb;
}
/**
* @param wb XSSFWorkBook对象
* @return 读取文件的sheet名与首行Title组装为json对象
*/
public JSONObject getTable(XSSFWorkbook wb){
JSONObject obj=new JSONObject();
JSONArray array=new JSONArray();
int SheetSum = wb.getNumberOfSheets();
for(int i=0;i<SheetSum;i++){
String sheetName=wb.getSheetName(i);
XSSFSheet sheet = wb.getSheetAt(i);
XSSFRow row = sheet.getRow(0);
if(row!=null) {
int cellsSum = row.getPhysicalNumberOfCells();
array.clear();
for (int j = 0; j < cellsSum; j++) {
array.add(row.getCell(j).getStringCellValue().trim());
}
obj.put(sheetName,array);
}
}
return obj;
}
/**
* @param poi 类对象
* @return 获取sheet页的全部数据
*/
public JSONArray getTableData(POIHelper poi){
JSONObject table = getTable(poi.getWb());
JSONArray array = JSONArray.fromObject(table.get(poi.getSheetName()));
JSONArray arr=new JSONArray();
JSONObject obj_sun =new JSONObject();
for(int i=1;i<poi.getRowSum();i++){
XSSFSheet sheet = poi.getSheet();
XSSFRow row = sheet.getRow(i);
obj_sun.clear();
for(int j=0;j<poi.getCellSum();j++){
XSSFCell cell = row.getCell(j);
//设置单元格类型
cell.setCellType(CellType.STRING);
obj_sun.put(array.get(j),cell.getStringCellValue().trim());
}
arr.add(obj_sun);
}
return arr;
}
将一张表的数据,每行数据读成一个JSON对象放入JSONArray中,类似下面的形式:
[{"id":"1","userName":"东邪","passWord":"123","gender":"男"},{"id":"2","userName":"西毒","passWord":"124","gender":"男"}]
JSONArray的好处是可以将其转化为映射类的List:
List user = JSONArray.toCollection(JSONArray对象, 映射类Class对象);
在映射类时,Excel sheet页和首行Title习惯上都会是汉字,而数据库和数据传输都是英语,中间存在一种映射,类似于国际化的转换。
Sheet页名与实体类名存在映射关系,sheet中首行字段转化 为英语后与实体类的属性存在映射。
在将JSONArray转化为List<类>时,需要根据sheet页名获取实体类名,通过Class.forName(固定实体类包名+类名)
public Class<?> getClazz(String entityName){
Class<?> clazz=null;
try {
clazz =Class.forName("com.soft.entity." + entityName);
} catch (Exception e) {
e.printStackTrace();
}
return clazz;
}
在service中进行批量插入时,调用DAO中方法,因为许多张表进行批量插入,所以不可避免地进行了硬编码
根据sheet名,选择不同的Dao接口批处理方法。另外有一个不好的地方,如果有十张表,但是只更新了两张表,依然会将十张表插入数据库。在后续进行扩展时,添加实体类时,同时需要修改业务部分的if条件判断,
是一个问题。写了两个类,User和product。
public void BatchInsertData(MultipartFile file){
try {
XSSFWorkbook wb = poiHelper.XSSFReadFile(file.getInputStream());
JSONObject table = poiHelper.getTable(wb);
Set<String> set = table.keySet();
for (String sheetName:set) {
POIHelper poi=new POIHelper(wb,sheetName);
JSONArray tableData = poi.getTableData(poi);
Class<?> clazz = entitymapper.getClazz(sheetName);
List list =(List) JSONArray.toCollection(tableData,clazz);
if(sheetName.equals("User")){
userDao.batchInsert(list);
}else if(sheetName.equals("Product")){
productDao.batchInsert(list);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
在最后献上mybatis,批量插入的经典SQL语句:ignore 可以避免多次重复插入相同数据时的冲突,相同时不进行插入。
<insert id="batchInsert" parameterType="java.util.List">
insert ignore into product(id,productName,price,description)
VALUES
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.id},#{item.productName},#{item.price},#{item.description}
)
</foreach>
</insert>
异常点:
在页面进行上传Excel时出现 exception is org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.soft.dao.UserDao.batchInsert。
检查mapper.xml后发现,mapper的namespace写的是实体类路径,应该是其对应DAO接口路径。修改后,问题排除。
另外,注意在maven项目中,mapper.xml 放置在resources下,不能放在java路径下。放错位置,也会导致上面找不到接口方法的问题。
需要完善点:
文件数据上传数据库前,文件格式、Sheet页、首行字段、数据等校验.
上文提到的硬编码问题