前言:最近練習簡單項目過程中使用Excel的導入導出較多,特來記錄一下相關代碼邏輯
准備工作
1. 首先在pom.xml文件中引入相關依賴
其他依賴若有需要可以引入
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--mybatis-plus 代碼生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
<!--mybatis-plus 代碼模板生成器引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!--阿里巴巴Easy Excel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<!-- xml -->
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
2. 在controller層中創建一個AdminAttendanceDictController.java文件
相關代碼如下
package com.xxx.student.core.controller.dict;
import com.alibaba.excel.EasyExcel;
import com.sun.deploy.net.URLEncoder;
import com.xxx.student.common.exception.BusinessException;
import com.xxx.student.common.result.R;
import com.xxx.student.common.result.ResponseEnum;
import com.xxx.student.core.pojo.dto.AttendanceExcelDTO;
import com.xxx.student.core.service.AttendanceService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Li
*/
@Api(tags = "數據字典管理")
@RestController
@Slf4j
@CrossOrigin
@RequestMapping("/admin/core/dictAttendance")
public class AdminAttendanceDictController {
@Resource
private AttendanceService attendanceService;
@ApiOperation("Excel日常考勤信息數據的導出")
@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 這里URLEncoder.encode可以防止中文亂碼 當然和easyExcel沒有關系
String fileName = URLEncoder
.encode("日常考勤數據字典列表", "UTF-8").replaceAll("\\+", "%20");
response
.setHeader("Content-disposition", "attachment;filename*=utf-8''" +
fileName + ".xlsx");
EasyExcel
.write(response.getOutputStream(), AttendanceExcelDTO.class)
.sheet("數據字典")
.doWrite(attendanceService.listDictData());
}
@ApiOperation("Excel日常考勤信息數據的批量導入")
@PostMapping("/import")
public R batchImport(@ApiParam(value = "Excel數據字典文件", required = true)
@RequestParam("file") MultipartFile file) {
try {
InputStream inputStream = file.getInputStream();
attendanceService.importData(inputStream);
return R.ok().message("數據字典批量導入成功");
} catch (Exception e) {
// 文件上傳錯誤
throw new BusinessException(ResponseEnum.UPLOAD_ERROR, e);
}
}
}
3.我們注意到AdminAttendanceExcelDto.java還未定義
所以我們需要新建一個AdminAttendanceExcelDto.java文件,代碼如下
package com.xxx.student.core.pojo.dto;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.xxx.student.core.util.LocalDateTimeConverter;
import lombok.Data;
import java.time.LocalDateTime;
/**
* <p>
*
* </p>
*
* @author lsh
* @since 2021-07-25
*/
@Data
@ContentRowHeight(15) //設置行高
@HeadRowHeight(20) //設置表頭高度
@ColumnWidth(20) //設置列寬
public class AttendanceExcelDTO {
@ExcelProperty(index = 0,value = "學生學號")
private Long studentNo;
@ExcelProperty(index = 1,value = "考勤時間",converter = LocalDateTimeConverter.class)
private LocalDateTime attendanceTime;
@ExcelProperty(index = 2,value = "考勤地點")
private String attendanceLocation;
@ExcelProperty(index = 3,value = "宿舍號")
private Integer dormitoryNo;
@ExcelIgnore
@DateTimeFormat("yyyy年MM月dd日HH時mm分ss秒")
@ExcelProperty(value = "創建時間")
private LocalDateTime createTime;
@ExcelIgnore
@ExcelProperty(value = "更新時間")
private LocalDateTime updateTime;
@ExcelIgnore
@ExcelProperty(value = "邏輯刪除")
private Boolean deleted;
}
4. 這里我們又使用到了一個用於轉換LocalDateTime的一個工具類
package com.xxx.student.core.util;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 自定義轉換器,轉換LocalDateTime
* @author Li
*/
public class LocalDateTimeConverter implements Converter<LocalDateTime> {
@Override
public Class<LocalDateTime> supportJavaTypeKey() {
return LocalDateTime.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public LocalDateTime convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
@Override
public CellData<String> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return new CellData<>(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
}
5.我們還需要創建一個AttendanceExcelDictDTOListener.java作為監聽器來幫助我們檢測導入的情況
繼承 AnalysisEventListener<AttendanceExcelDTO>,泛型為我們要實現的ExcelDTO對象
package com.xxx.student.core.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.CollectionUtils;
import com.xxx.student.core.mapper.AttendanceMapper;
import com.xxx.student.core.pojo.dto.AttendanceExcelDTO;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
/**
* @author Li
*/
@Slf4j
@NoArgsConstructor
public class AttendanceExcelDictDTOListener extends AnalysisEventListener<AttendanceExcelDTO> {
// 數據列表
ArrayList<AttendanceExcelDTO> list = new ArrayList<>();
private AttendanceMapper attendanceMapper;
// 每隔10條數據批量存入一次數據
static final int BATCH_COUNT = 5;
// 每次創建Listener的時候需要把spring管理的類傳進來
public AttendanceExcelDictDTOListener(AttendanceMapper attendanceMapper){
this.attendanceMapper = attendanceMapper;
}
@Override
public void invoke(AttendanceExcelDTO data, AnalysisContext context) {
log.info("解析到一條記錄:{}", data);
// 將數據存入到數據列表
list.add(data);
// 達到BATCH_COUNT了,需要去存儲一次數據庫,防止數據幾萬條數據在內存,容易OOM
if(list.size() >= BATCH_COUNT){
// 調用mapper層的save方法
savaData();
// 存儲完成清理 list
list.clear();
}
}
/**
* 所有數據解析完成了 都會來調用
*
* @param context .
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 當剩余的記錄數不足BATCH_COUNT時,最終一次性存儲數據
savaData();
log.info("所有數據解析完成");
}
private void savaData(){
log.info("{} 條數據存儲到數據庫", list.size());
// 調用mapper層的save方法 SAVE list對象
if(!CollectionUtils.isEmpty(list)) {
attendanceMapper.insertBatch(list);
}
//TODO
log.info("{} 條數據被存儲到數據庫成功", list.size());
}
}
6.service層創建一個名為AttendanceService的接口文件部分代碼如下
package com.xxx.student.core.service;
import com.xxx.student.core.pojo.dto.AttendanceExcelDTO;
import com.xxx.student.core.pojo.entity.Attendance;
import com.baomidou.mybatisplus.extension.service.IService;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
* <p>
* 服務類
* </p>
*
* @author lsh
*/
public interface AttendanceService extends IService<Attendance> {
/**
*
* @return Excel導出
*/
List<AttendanceExcelDTO> listDictData();
/**
*
* @param inputStream Excel導入
*/
void importData(InputStream inputStream);
}
7.ServiceImpl部分代碼如下
@Slf4j
@Service
public class AttendanceServiceImpl extends ServiceImpl<AttendanceMapper, Attendance> implements AttendanceService {
private final QueryWrapper<Attendance> attendanceQueryWrapper = new QueryWrapper<>();
@Resource
private AttendanceMapper attendanceMapper;
@Override
public List<AttendanceExcelDTO> listDictData() {
List<Attendance> list = baseMapper.selectList(null);
//創建ExcelDictDTO列表,將Dict列表轉換成ExcelDictDTO列表
ArrayList<AttendanceExcelDTO> excelDictDTOList = new ArrayList<>(list.size());
// 遍歷dict列表
list.forEach(dict -> {
AttendanceExcelDTO attendanceExcelDTO = new AttendanceExcelDTO();
BeanUtils.copyProperties(dict, attendanceExcelDTO);
excelDictDTOList.add(attendanceExcelDTO);
});
return excelDictDTOList;
}
@Override
public void importData(InputStream inputStream) {
EasyExcel
.read(inputStream, AttendanceExcelDTO.class, new AttendanceExcelDictDTOListener(baseMapper))
.excelType(ExcelTypeEnum.XLSX)
.sheet()
.doRead();
log.info("Excel導入成功");
}
}
8.mapper層
/**
*
* @param list 批量插入
*/
void insertBatch(ArrayList<AttendanceExcelDTO> list);
9.mapper.xml文件
<insert id="insertBatch">
INSERT INTO `student_teacher_info`.`attendance`(`student_No`, `attendance_Time`, `attendance_Location`, `dormitory_No`)
VALUES<foreach collection="list" separator="," item="item" index="index">(#{item.studentNo},
#{item.attendanceTime},
#{item.attendanceLocation},
#{item.dormitoryNo})</foreach>
</insert>
10.實體類Attendance
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="Attendance對象", description="日常考勤")
public class Attendance implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "學生學號")
@TableId(value = "student_No", type = IdType.INPUT) // 用戶輸入
private Long studentNo;
@JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = " 考勤時間")
@TableField("attendance_Time")
private LocalDateTime attendanceTime;
@ApiModelProperty(value = "考勤地點")
@TableField("attendance_Location")
private String attendanceLocation;
@ApiModelProperty(value = "宿舍號")
@TableField("dormitory_No")
private Integer dormitoryNo;
@ApiModelProperty(value = "創建時間",example = " ")
private LocalDateTime createTime;
@ApiModelProperty(value = "更新時間",example = " ")
private LocalDateTime updateTime;
@ApiModelProperty(value = "邏輯刪除(1:已刪除,0:未刪除)")
@TableField("is_deleted")
@TableLogic
private Boolean deleted;
}
至此:導入導出模塊基本完成