Excel表单导入,是将Excel文件中的数据导入到数据库;而Excel表单导出,是将数据库的数据导出到Excel文件中。
在本文中开发的功能是基于SpringBoot + JPA框架进行开发,而在系统中,数据库表结构又分成了两种情况,一种是单表结构,另一种是存在@OneToMany
注解的主外键关联的主辅表结构,所以本次开发目的是可以同时兼容两种情况。
另外,因为不同的数据表对应的Excel表单的数据和格式都不同,但是为每一个数据表定制化开发Excel导入导出,工作重复度高,所以本次开发另一个目的是能够做到所有数据表通用Excel表单导入导出。
SpringBoot和JPA的环境请读者自行准备,在这里引入poi依赖来协助开发,版本选择4.0.1。
org.apache.poi
poi
${poi.version}
org.apache.poi
poi-ooxml
${poi.version}
导入和导出功能都能通过传入的服务参数,完成对指定表结构的导入或导出。
如:http://127.0.0.1/excel/{SERVER_NAME}/import (导入)
http://127.0.0.1/excel/{SERVER_NAME}/export (导出)
一、Excel导出
1、导出目标
可以导出两种形式的Excel文件:
(1)数据模版,用于数据填入后进行Excel导入功能,命名规则为:Excel-‘name’-Template.xlsx(如:Excel-Project-Template.xlsx);
(2)指定时间区间的数据,命名规则为:Excel-‘name’.xlsx(如:Excel-Project.xlsx)。
2、导出格式
将数据表的字段以字段名+‘-’的格式(如:id-),依次插入到表单的首行单元格。如果导出的是指定区间的数据,则会在首行行尾追加’createTime’和‘createBy’两个字段,分别表示数据创建时间和数据创建人(关联关系表结构中,只在主表中追加),再将数据逐行插入。
*注:表头字段的设计(如:id-),是为了方便用户在填入自己需要导入数据的过程中,可以在表头字段后加上自己的注解(如:id-编号),并且不影响Excel表单的数据导入,数据填入过程中,请保留’-'符号。
3、导出结构
单表结构:在Entity字段定义中,不存在@OneToMany注解,在该结构下,只有一张Excel工作簿。
关联关系表结构:在Entity字段定义中,存在@OneToMany注解,在该结构下,需要新建Excel工作薄,再将字段或数据填入新的工作簿中。
二、Excel导入
1、导入规则
将数据表的字段以字段名+‘-’的格式(如:id-),依次插入到表单的首行单元格,再将数据逐行填入字段对应的同列单元格即可。表头字段名后可以加上自己的注解(如:id-编号),不影响表单的正常导入。
*注:为避免用户由于Excel表单格式的问题,数据导入失败,请用户先使用Excel导出功能,导出一份表单模板,再将数据填入模板后进行导入操作,保证数据可正常导入。
*注:若导入的数据为关联关系表结构,则辅表工作簿中的关联字段需要和主表工作薄中的主键保持一致。
2、导入操作
根据@Id注解和@GeneratedValue注解判断主键的类型,对主键做特殊处理,若是自增主键,会将主键置为空,保证数据只做插入操作,不做更新操作;而若是非自增主键,不做处理,数据可以进行插入操作,或者对数据库已经存在该主键的数据进行更新操作。
3、导入反馈
在数据转化后,会将数据逐条存入数据库,而在这个过程中,可能会存在数据的错误或者别的问题导致部分数据的存储失败,但是部分数据的存储失败不会导致整个Excel表单数据存储的失败回滚,而是将存储失败的数据对应的Excel表单单元行和失败原因反馈给用户,如:导入数据总数,成功导入数据总数,失败导入数据总数以及失败导入原因。
(1)RIA报表服务抽象接口
/**
* RIA报表服务抽象接口
* @author xinyao.zhang
*/
public interface BaseRiaExcelService {
/**
* 导入Excel文件数据
* @param file 文件
* @return JSONObject
*/
default JSONObject importExcelData(MultipartFile file){return null;}
/**
* Excel文件 转List数据
* @param file
* @return
*/
default List excelDataConvert(MultipartFile file){return null;}
/**
* 导出Excel文件数据
* @param flag 0:导出Excel格式 1:导出指定时间区间数据
* @param beginTime 开始时间
* @param endTime 结束时间
* @param response 返回数据
*/
default void exportExcelData(Integer flag, Long beginTime, Long endTime, HttpServletResponse response){}
/**
* 数据构建Excel文件
* @param flag
* @param beginTime
* @param endTime
*/
default XSSFWorkbook dataToExcel(Integer flag, Long beginTime, Long endTime){return null;}
}
(2)RIA基础服务类
/**
* RIA基础服务类
* @author xinyao.zhang
*/
public interface BaseRiaService extends BaseService,BaseRiaExcelService{
}
(3)基础服务接口
/**
* 基础服务接口
* @author xinyao.zhang
*/
public interface BaseService {
/**
* 查找单个实例
* @param id 实例ID
* @return T
*/
T find(ID id);
}
(4)基础服务逻辑层
/**
* 基础服务逻辑层
* @author xinyao.zhang
*/
public class BaseServiceImpl implements BaseService {
private final Logger logger = LoggerFactory.getLogger(getClass());
protected Class entityClass = (Class) GenericClassUtils.getSuperClassGenericType(getClass(), 0);
protected Class idClass = (Class) GenericClassUtils.getSuperClassGenericType(getClass(), 1);
protected BaseJpaRepository baseJpaRepository;
public BaseServiceImpl(BaseJpaRepository baseJpaRepository){
this.baseJpaRepository = baseJpaRepository;
}
@Override
public T find(ID id){
T optional = null;
//ID类型转换
if(idClass.isInstance(id)){
optional = baseJpaRepository.findOne(idClass.cast(id));
}else{
switch (idClass.getName()) {
case "java.lang.String":
optional = baseJpaRepository.findOne(idClass.cast(String.valueOf(id)));
break;
case "java.lang.Integer":
optional = baseJpaRepository.findOne(idClass.cast(Integer.valueOf(id.toString())));
break;
case "java.lang.Long":
optional = baseJpaRepository.findOne(idClass.cast(Long.valueOf(id.toString())));
break;
default:
break;
}
}
if(null != optional){
return optional;
}
return null;
}
}
(5)泛型工具类
/**
* 泛型工具类
* @author xinyao.zhang
*/
@SuppressWarnings("rawtypes")
public class GenericClassUtils {
public static Class getSuperClassGenericType(Class clazz, int index) {
Type genType = clazz.getGenericSuperclass();
if (!(genType instanceof ParameterizedType)) {
return null;
}
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0) {
return null;
}
if (!(params[index] instanceof Class)) {
return null;
}
return (Class) params[index];
}
}
(6)RIA基础实现基类
/**
* RIA基础实现基类
* @author xinyao.zhang
* @param
* @param
*/
@SuppressWarnings("unchecked")
public abstract class BaseRiaServiceImpl extends BaseServiceImpl implements BaseRiaService {
private static final Logger logger = LoggerFactory.getLogger(BaseRiaServiceImpl.class);
private BaseRiaExcel tBaseRiaExcel;
public BaseRiaServiceImpl(BaseJpaRepository baseJpaRepository) {
super(baseJpaRepository);
tBaseRiaExcel = new BaseRiaExcel(entityClass,idClass,baseJpaRepository);
}
protected BaseRiaExcel baseRiaExcel() {
return tBaseRiaExcel;
}
@Override
public JSONObject importExcelData(MultipartFile file) {
return tBaseRiaExcel.importExcelData(excelDataConvert(file));
}
@Override
public List excelDataConvert(MultipartFile file) {
return tBaseRiaExcel.excelDataConvert(file);
}
@Override
public void exportExcelData(Integer flag, Long beginTime, Long endTime, HttpServletResponse response) {
tBaseRiaExcel.exportExcelData(dataToExcel(flag, beginTime, endTime), flag, response);
}
@Override
public XSSFWorkbook dataToExcel(Integer flag, Long beginTime, Long endTime) {
return tBaseRiaExcel.dataToExcel(flag, beginTime, endTime);
}
}
(7)RIA报表服务类
/**
* RIA报表服务类
* @author xinyao.zhang
* @param
* @param
*/
public class BaseRiaExcel extends BaseServiceImpl {
private static final Logger logger = LoggerFactory.getLogger(BaseRiaExcel.class);
private Class entityClass;
public BaseRiaExcel(Class entityClass,Class idClass,BaseJpaRepository baseJpaRepository){
super(baseJpaRepository);
super.entityClass = entityClass;
super.idClass = idClass;
this.entityClass = entityClass;
}
/**
* List数据导入数据库
* @param list
* @return
*/
public JSONObject importExcelData(List list) {
JSONObject result = new JSONObject();
try{
JSONArray error = null;
int successCount = 0;
int failCount = 0;
for(int index = 0; index < list.size(); index++) {
int rowNum = index + 2;
//保存数据
try{
T entity = list.get(index);
boolean isUpdate = false;
Field[] fields = entityClass.getDeclaredFields();
for(Field f: fields){
//判断id主键是否置空
Id id = f.getAnnotation(Id.class);
if(null != id){
f.setAccessible(true);
Object idObject = f.get(entity);
if(null != idObject){
String idValue = String.valueOf(idObject);
T oldEntity = (T)find(idValue);
//id主键没有置空且存在旧数据,进行更新操作
if(null != oldEntity){
BeanCopyUtils.copyPropertiesIgnoreNull(oldEntity, entity);
baseJpaRepository.saveAndFlush(oldEntity);
isUpdate = true;
}
}
break;
}
}
if(!isUpdate){
baseJpaRepository.saveAndFlush(entity);
}
logger.info("单元行:{},数据保存成功", rowNum);
successCount++;
}catch (Exception e){
if(null == error){
error = new JSONArray();
}
logger.error("单元行:{},数据保存失败", rowNum, e);
error.add("单元行:" + rowNum + ",数据保存失败," + e.getMessage());
failCount++;
}
}
result.put("rowsCount", list.size());
result.put("successCount", successCount);
result.put("failCount", failCount);
if(null != error){
result.put("error", error);
}
} catch (Exception e){
throw new BusinessException("Excel数据导入失败");
}
return result;
}
/**
* Excel文件数据转List
* @param file
* @return
*/
public List excelDataConvert(MultipartFile file){
List list;
try{
XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file.getInputStream());
int sheetNumber = xssfWorkbook.getNumberOfSheets();
if(sheetNumber > 1){
list = multipleSheetDataConvert(xssfWorkbook);
}else {
list = singleSheetDataConvert(xssfWorkbook);
}
} catch (Exception e){
throw new BusinessException("Excel数据导入失败");
}
return list;
}
/**
* 单工作薄数据处理
* @param xssfWorkbook
* @return
*/
private List singleSheetDataConvert(XSSFWorkbook xssfWorkbook){
List list = new ArrayList<>();
//默认:第一页工作薄
XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
//首行头信息获取
XSSFRow firstRow = xssfSheet.getRow(0);
for(int rowNum = 1; rowNum <= xssfSheet.getLastRowNum(); rowNum++) {
//行数据获取
XSSFRow dataRow = xssfSheet.getRow(rowNum);
try{
T entity = convertToEntity(firstRow, dataRow);
list.add(entity);
}catch (Exception e){
logger.error("Excel单元行:{},转化Bean失败", rowNum + 1, e);
}
}
return list;
}
/**
* 数据行转化Bean
* @param firstRow 首行头信息
* @param dateRow 行数据
* @return
*/
private T convertToEntity(XSSFRow firstRow, XSSFRow dateRow){
T entity;
try{
JSONObject jsonObject = new JSONObject();
for(int cellNum = 0; cellNum < dateRow.getLastCellNum(); cellNum++){
//获取单元格数据
XSSFCell cell = dateRow.getCell(cellNum);
String cellValue = getCellValue(cell);
if(null == cellValue){
continue;
}
//获取头信息属性
XSSFCell headCell = firstRow.getCell(cellNum);
String headCellValue = getCellValue(headCell);
String field = headCellValue.split("-")[0];
jsonObject.put(field, cellValue);
//去除自增主键
Id id = entityClass.getDeclaredField(field).getAnnotation(Id.class);
if(null != id){
//自增主键判断
GeneratedValue generatedValue = entityClass.getDeclaredField(field).getAnnotation(GeneratedValue.class);
if(null != generatedValue && GenerationType.SEQUENCE.equals(generatedValue.strategy())){
jsonObject.remove(field);
}
}
}
entity = JSONObject.parseObject(jsonObject.toJSONString(), entityClass);
return entity;
}catch (Exception e){
logger.error("Excel单元行转化Bean失败,{}", dateRow, e);
return null;
}
}
/**
* 多工作薄,存在关联数据
* 转化为主表Bean List数据
* @param xssfWorkbook
* @return
*/
private List multipleSheetDataConvert(XSSFWorkbook xssfWorkbook){
try{
List list = new ArrayList<>();
Field[] fields = entityClass.getDeclaredFields();
Field idField = null;
//存储关联数据
Map map = new HashMap<>();
//存储关联表的关联字段
Map columnMap = new HashMap<>();
//存储关联表的类
Map classMap = new HashMap<>();
//存储关联表在主表中的属性
Map fieldMap = new HashMap<>();
//存储关联表中的自增id
Map idMap = new HashMap<>();
//遍历主表属性
for(Field field: fields){
Id id = field.getAnnotation(Id.class);
if(null != id){
idField = field;
continue;
}
//判断是否一对多映射
OneToMany oneToMany = field.getAnnotation(OneToMany.class);
if(null != oneToMany){
JoinColumn joinColumn = field.getAnnotation(JoinColumn.class);
if(null != joinColumn){
String fieldName = underlineToCamel(joinColumn.name());
Class clazz = oneToMany.targetEntity();
String entityName = clazz.getSimpleName();
//实例化map存储关联数据
Map entityMap = new HashMap<>();
map.put(entityName, entityMap);
columnMap.put(entityName, fieldName);
classMap.put(entityName, clazz);
fieldMap.put(entityName, field);
Field[] entityFields = clazz.getDeclaredFields();
for(Field f: entityFields){
Id entityId = f.getAnnotation(Id.class);
if(null != entityId){
//自增主键判断
GeneratedValue generatedValue = f.getAnnotation(GeneratedValue.class);
if(null != generatedValue && GenerationType.SEQUENCE.equals(generatedValue.strategy())){
idMap.put(entityName, f.getName());
}
break;
}
}
}
}
}
if(null == idField){
logger.error("Bean没有Id注解属性");
throw new BusinessException("Bean没有Id注解属性");
}
//遍历工作簿
for(int sheetNum = 0; sheetNum < xssfWorkbook.getNumberOfSheets(); sheetNum++){
XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(sheetNum);
String sheetName = xssfSheet.getSheetName();
//首行头信息获取
XSSFRow firstRow = xssfSheet.getRow(0);
//行数据获取
for(int rowNum = 1; rowNum <= xssfSheet.getLastRowNum(); rowNum++){
XSSFRow xssfRow = xssfSheet.getRow(rowNum);
JSONObject jsonObject = new JSONObject();
String relationId = null;
//单元格数据获取
for(int cellNum = 0; cellNum < xssfRow.getLastCellNum(); cellNum++){
//获取单元格数据
XSSFCell cell = xssfRow.getCell(cellNum);
String cellValue = getCellValue(cell);
if(null == cellValue){
continue;
}
//获取头信息属性
XSSFCell headCell = firstRow.getCell(cellNum);
String headCellValue = getCellValue(headCell);
String field = headCellValue.split("-")[0];
jsonObject.put(field, cellValue);
//判断是否是关联表中关联字段
if(columnMap.containsKey(sheetName)){
String column = columnMap.get(sheetName);
if(field.equals(column)){
relationId = cellValue;
}
}
}
try{
//关联数据处理
if(map.containsKey(sheetName) && classMap.containsKey(sheetName) && null != relationId){
Map entityMap = map.get(sheetName);
if(null == entityMap.get(relationId)){
Field columnField = fieldMap.get(sheetName);
Collection collection = getFieldCollection(columnField);
entityMap.put(relationId, collection);
}
//去除自增主键
if(idMap.containsKey(sheetName)){
String id = idMap.get(sheetName);
jsonObject.put(id, null);
}
//去除关联字段
String column = columnMap.get(sheetName);
jsonObject.put(column, null);
entityMap.get(relationId).add(JSONObject.parseObject(jsonObject.toJSONString(), classMap.get(sheetName)));
}else {
T entity = JSONObject.parseObject(jsonObject.toJSONString(), entityClass);
list.add(entity);
}
}catch (Exception e){
logger.error("Excel单元行:{},转化Bean失败", rowNum + 1, e);
}
}
}
boolean idIsSequence = false;
GeneratedValue generatedValue = idField.getAnnotation(GeneratedValue.class);
if(null != generatedValue && GenerationType.SEQUENCE.equals(generatedValue.strategy())){
idIsSequence = true;
}
//设置关联数据
for(int index = 0; index < list.size(); index++){
int num = index + 2;
try{
T t = list.get(index);
idField.setAccessible(true);
String id = String.valueOf(idField.get(t));
//遍历关联属性
for(Map.Entry entry: fieldMap.entrySet()){
String entityName = entry.getKey();
Field field = entry.getValue();
Map entityMap = map.get(entityName);
Collection entity = entityMap.get(id);
field.setAccessible(true);
field.set(t, entity);
}
//去除自增主键
if(idIsSequence){
idField.set(t, null);
}
}catch (Exception e){
logger.error("Excel单元行:{},转化Bean失败", num, e);
}
}
return list;
}catch (Exception e){
logger.error("Excel数据行转化Bean失败", e);
throw new BusinessException("Excel数据行转化Bean失败");
}
}
/**
* 下划线格式字符串转换为驼峰格式字符串
* @param param
* @return
*/
private static String underlineToCamel(String param) {
if (param == null || "".equals(param.trim())) {
return "";
}
int len = param.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c = param.charAt(i);
if (c == '_') {
if (++i < len) {
sb.append(Character.toUpperCase(param.charAt(i)));
}
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* 获取关联属性的存储集合
* @param field
* @return
*/
private Collection getFieldCollection(Field field){
if(null == field){
return null;
}
Collection collection = null;
Class classType = field.getType();
if(classType.isAssignableFrom(List.class)){
collection = new ArrayList();
} else if(classType.isAssignableFrom(Set.class)){
collection = new ArrayList();
}
return collection;
}
/**
* 获取单元格数据
* @param cell
* @return
*/
private String getCellValue(XSSFCell cell){
if(null == cell){
return null;
}
String result = null;
switch (cell.getCellType()){
case NUMERIC:
if(DateUtil.isCellDateFormatted(cell)){
Date createDate = cell.getDateCellValue();
result = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createDate);
}else {
cell.setCellType(CellType.STRING);
result = cell.getStringCellValue();
}
break;
case FORMULA:
cell.setCellType(CellType.STRING);
result = cell.getStringCellValue();
break;
case STRING:
cell.setCellType(CellType.STRING);
result = cell.getStringCellValue();
break;
case BOOLEAN:
cell.setCellType(CellType.BOOLEAN);
result = String.valueOf(cell.getBooleanCellValue());
break;
case _NONE:
break;
case BLANK:
break;
case ERROR:
break;
default:
break;
}
return result;
}
/**
* 导出Excel文件
* @param xssfWorkbook
* @param flag
* @param response
*/
public void exportExcelData(XSSFWorkbook xssfWorkbook, Integer flag, HttpServletResponse response) {
if(null == xssfWorkbook){
return;
}
OutputStream out = null;
try{
String name = entityClass.getSimpleName();
if(flag.equals(0)){
name += "-Template";
}
//设置response信息
String title = "Excel-" + name + ".xlsx";
String headStr = "attachment; filename=\"" + new String(title.getBytes("gb2312"), "UTF-8" ) + "\"";
response.setContentType("octets/stream");
response.setContentType("APPLICATION/OCTET-STREAM");
response.setHeader("Content-Disposition", headStr);
//导出Excel
out = response.getOutputStream();
xssfWorkbook.write(out);
}catch (Exception e){
logger.error("Excel文件导出失败", e);
}finally {
if(null != out){
try {
out.close();
}catch (Exception e){
logger.error("OutputStream流关闭失败", e);
}
}
}
}
/**
* 数据构建Excel文件
* @param flag
* @param beginTime
* @param endTime
* @return
*/
public XSSFWorkbook dataToExcel(Integer flag, Long beginTime, Long endTime){
if(!flag.equals(0) && !flag.equals(1)){
return null;
}
String name = entityClass.getSimpleName();
Field[] fields = entityClass.getDeclaredFields();
//构建Excel
XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
XSSFSheet xssfSheet = xssfWorkbook.createSheet(name);
xssfSheet.setDefaultColumnWidth(20);
//设置单元格样式
XSSFCellStyle style = xssfWorkbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
XSSFFont font = xssfWorkbook.createFont();
font.setBold(true);
font.setFontHeight(12);
style.setFont(font);
Map entityNamesMap = new HashMap<>();
Map entityFieldsMap = new HashMap<>();
//首行插入字段
XSSFRow firstRow = xssfSheet.createRow(0);
for(int i = 0; i < fields.length; i++){
Field field = fields[i];
//判断是否一对多映射
OneToMany oneToMany = field.getAnnotation(OneToMany.class);
if(null != oneToMany){
Class clazz = oneToMany.targetEntity();
//新建工作簿
String entityName = clazz.getSimpleName();
XSSFSheet entitySheet = xssfWorkbook.createSheet(entityName);
entitySheet.setDefaultColumnWidth(20);
Field[] entityFields = clazz.getDeclaredFields();
entityFieldsMap.put(field.getName(), entityFields);
entityNamesMap.put(field.getName(), entityName);
//首行插入字段
XSSFRow entityFirstRow = entitySheet.createRow(0);
for(int cellNum = 0; cellNum < entityFields.length; cellNum++){
XSSFCell cell = entityFirstRow.createCell(cellNum);
cell.setCellValue(entityFields[cellNum].getName() + "-");
cell.setCellStyle(style);
}
continue;
}
XSSFCell xssfCell = firstRow.createCell(i);
xssfCell.setCellValue(field.getName() + "-");
xssfCell.setCellStyle(style);
}
//flag:1
if(flag.equals(1)){
//新增createTime字段单元格
XSSFCell createTimeCell = firstRow.createCell(firstRow.getLastCellNum());
createTimeCell.setCellValue("createTime-");
createTimeCell.setCellStyle(style);
//新增createBy字段单元格
XSSFCell createByCell = firstRow.createCell(firstRow.getLastCellNum());
createByCell.setCellValue("createBy-");
createByCell.setCellStyle(style);
//获取时间区间数据
List list = baseJpaRepository.findAll((root,query,cb) -> {
List predicateList = new ArrayList<>();
predicateList.add(cb.between(root.get("createTime"), new Date(beginTime), new Date(endTime)));
Predicate[] array = new Predicate[predicateList.size()];
return cb.and(predicateList.toArray(array));
});
font.setBold(false);
style.setFont(font);
int dataIndex = 1;
for (T e : list) {
JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(e));
//构建新的单元行
XSSFRow xssfRow = xssfSheet.createRow(dataIndex++);
//插入常规数据
for (int cellNum = 0; cellNum < fields.length; cellNum++) {
Field field = fields[cellNum];
String fieldName = field.getName();
//判断是否为关联属性
if(entityFieldsMap.containsKey(fieldName) && entityNamesMap.containsKey(fieldName)){
//关联工作薄处理数据
XSSFSheet entitySheet = xssfWorkbook.getSheet(entityNamesMap.get(fieldName));
JSONArray entityArray = JSONArray.parseArray(String.valueOf(jsonObject.get(fieldName)));
entityArray.forEach(entity -> {
JSONObject entityJson = JSONObject.parseObject(JSON.toJSONString(entity));
//构建新的单元行
XSSFRow entityRow = entitySheet.createRow(entitySheet.getLastRowNum() + 1);
Field[] entityFields = entityFieldsMap.get(fieldName);
for(int entityCellNum = 0; entityCellNum < entityFields.length; entityCellNum++){
Field entityField = entityFields[entityCellNum];
String entityFieldName = entityField.getName();
XSSFCell cell = entityRow.createCell(entityCellNum);
cell.setCellValue(getFieldData(entityField, entityJson.get(entityFieldName)));
cell.setCellStyle(style);
}
});
continue;
}
XSSFCell xssfCell = xssfRow.createCell(cellNum);
xssfCell.setCellValue(getFieldData(field, jsonObject.get(fieldName)));
xssfCell.setCellStyle(style);
}
//插入createTime数据
XSSFCell createTimeDataCell = xssfRow.createCell(xssfRow.getLastCellNum());
createTimeDataCell.setCellValue(DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.CHINA).format(jsonObject.get("createTime")));
createTimeDataCell.setCellStyle(style);
//插入createBy数据
XSSFCell createByDataCell = xssfRow.createCell(xssfRow.getLastCellNum());
createByDataCell.setCellValue(String.valueOf(jsonObject.get("createBy")));
createByDataCell.setCellStyle(style);
}
}
return xssfWorkbook;
}
/**
* 转化属性值为String
* @param field
* @param object
* @return
*/
protected String getFieldData(Field field, Object object){
if(null == field || null == object){
return null;
}
String result;
String type = field.getGenericType().toString();
switch (type){
case "class java.util.Date":
result = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.CHINA).format(object);
break;
default:
result = String.valueOf(object);
break;
}
return result;
}
}
/**
* Excel服务接口
* @author xinyao.zhang
*/
public interface RiaExcelService {
/**
* 导入Excel文件数据
* @param serviceName 服务名
* @param file 文件
* @return JSONObject
*/
JSONObject importExcelData(String serviceName,MultipartFile file);
/**
* 导出Excel文件数据
* @param serviceName 服务名
* @param exportExcelDto 导出参数
*/
void exportExcelData(String serviceName, ExportExcelDto exportExcelDto);
}
(2)Excel服务实现类
/**
* Excel服务实现类
* @author chenjianian
*/
@Service
public class RiaExcelServiceImpl extends RiaServiceImpl implements RiaExcelService {
@Override
public JSONObject importExcelData(String serviceName,MultipartFile file) {
BaseRiaService baseRiaService = super.riaBaseService(serviceName);
return baseRiaService.importExcelData(file);
}
@Override
public void exportExcelData(String serviceName, ExportExcelDto exportExcelDto) {
BaseRiaService baseRiaService = super.riaBaseService(serviceName);
baseRiaService.exportExcelData(exportExcelDto.getFlag()
,exportExcelDto.getBeginTime(),exportExcelDto.getEndTime(),exportExcelDto.getResponse());
}
}
(3)RIA服务实现类
/**
* RIA服务实现类
* @author xinyao.zhang
*/
public class RiaServiceImpl{
/**
* 获取流程对应服务
* @param riaServiceName 流程服务名
* @return RiaBaseService
*/
protected BaseRiaService riaBaseService(String riaServiceName){
ApplicationContext applicationContext = ApplicationContextRegister.getApplicationContext();
return (BaseRiaService) applicationContext.getBean(riaServiceName);
}
}
(4)应用上下文配置
/**
* 应用上下文配置
* @author xinyao.zhang
*/
@Component
@Lazy(false)
public class ApplicationContextRegister implements ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationContextRegister.class);
private static ApplicationContext APPLICATION_CONTEXT;
/**
* 设置spring上下文 * * @param applicationContext spring上下文 * @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
LOGGER.debug("ApplicationContext registed-->{}", applicationContext);
APPLICATION_CONTEXT = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return APPLICATION_CONTEXT;
}
}
(5)表单-控制层
/**
* 表单-控制层
* @author chenjianian
*/
@RestController
@RequestMapping("excel")
public class ExcelModelController {
@Resource
private RiaExcelService riaExcelService;
/**
* 导入Excel文件数据
* @param file
* @return ResultDataDto
*/
@ApiOperation(value = "导入Excel数据", notes = "根据导入的Excel文件导入数据到数据库")
@RequestMapping(value = "/{MODEL_NAME}/import", method = RequestMethod.POST)
@ResponseBody
public ResultDataDto importExcelData(@PathVariable("MODEL_NAME") String serviceName,@RequestParam("file") MultipartFile file){
String fileName = file.getOriginalFilename();
if(!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx")){
throw new BusinessException(CommonConstants.EXCEPTION_STATUS, "非法文件格式");
}
try {
JSONObject result = riaExcelService.importExcelData(serviceName,file);
ResultDataDto resultDataDto = new ResultDataDto();
resultDataDto.setCode(CommonConstants.SUCCESS_CODE);
resultDataDto.setMessage("数据导入完成");
resultDataDto.setStatus(CommonConstants.SUCCESS_STATUS);
resultDataDto.setData(result);
return resultDataDto;
} catch (Exception e){
throw new BusinessException(CommonConstants.EXCEPTION_STATUS, CommonConstants.EXCEPTION_MESSAGE + e.getMessage(), e);
}
}
/**
* 导出Excel文件数据
* @param serviceName 模块名
* @param exportExcelDto 导出参数
*/
@ApiOperation(value = "导出Excel数据", notes = "将数据库数据导出到Excel文件")
@RequestMapping(value = "/{MODEL_NAME}/export", method = RequestMethod.POST)
@ResponseBody
public void exportExcelData(@PathVariable("MODEL_NAME") String serviceName, @RequestBody @Valid ExportExcelDto exportExcelDto, HttpServletResponse response){
try {
exportExcelDto.setResponse(response);
riaExcelService.exportExcelData(serviceName,exportExcelDto);
} catch (Exception e){
throw new BusinessException(CommonConstants.EXCEPTION_STATUS, CommonConstants.EXCEPTION_MESSAGE + e.getMessage(), e);
}
}
}
在业务Service层,只需继承BaseRiaServiceImpl类,即拥有了通用的Excel功能,而接口SERVICE_NAME只需传入服务的名字及可完成对应Excel功能的调用。在此引入一个例子,如:职员-服务EmployeeServiceImpl。
/**
* 职员-服务实现层
*
* @author xinyao.zhang
*/
@Service("employee")
public class EmployeeServiceImpl extends BaseRiaServiceImpl {
}
*注:可重写RIA报表服务抽象接口BaseRiaExcelService,在通用的基础上完成定制化的导入导出开发。
职员Employee
一、导出数据模版
二、填入数据后导入
数据导出部分在这里不做演示。
本次的开发,实现了对单表结构和存在主外键关联的主辅表结构的通用Excel导入和导出功能,用户可以选择参数的传入控制相应数据表的导入和导出。
在功能支持上,自增主键表结构,仅支持对应数据的插入操作,不支持对数据的更新操作;非自增主键表结构,支持对应数据的插入操作,也支持对数据的更新操作。