一、前言
今天小編帶大家一起整合一下easyExcel
,之所以用這個,是因為easyExcel
效能比較好,不會報OOM
!
市面上常見的匯入匯出Excel分為三種:
- hutool
- easyExcel
- poi
hutool
和easyExcel
都是對poi
的封裝,使用起來更加方便!
如果想使用poi
和hutool
匯出的可以看一下小編的之前寫的文章:
使用POI+hutool匯入Excel
使用POI把查詢到的資料表資料匯出到Excel中,一個表一個sheet
匯出的話看一下這篇,下面主要以匯入來展開介紹!
EasyExcel匯出Excel表格到瀏覽器,並透過Postman測試匯出Excel
二、匯入依賴
小編這裡是3.0.X
版本的,版本不同可能導致部分有出入,如果大家版本是3.1.X
,可以去官方文件看看有不一樣的!
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.0.5</version>
</dependency>
三、實體類
這裡可以自帶的轉換器:
- @DateTimeFormat("yyyy年MM月dd日HH時mm分ss秒")
- LocalDateTimeStringConverter
或者自定義轉化器:
實現:implements Converter<T>
具體文件:官方文件
@ExcelProperty
引數注意:
這裡不建議 index 和 name 同時用,要麼一個物件只用index,要麼一個物件只用name去匹配
用名字去匹配,這裡需要注意,如果名字重複,會導致只有一個欄位讀取到資料
/**
* @author wangzhenjun
* @date 2022/12/2 15:52
*/
@Data
public class Test {
@TableId
private Integer id;
@ExcelProperty(index = 0)
private String name;
@ExcelProperty(index = 1)
private Integer age;
@ExcelProperty(index = 2,converter = LocalDateTimeStringConverter.class)
private LocalDateTime time;
}
四、編寫監聽器
注意點:
這個監聽器一定不要是單例的,被spring管理預設為單例,如果要使用@Component
,一定要加上:
@Scope("prototype")
,這樣在建立完後spring不會進行管理,每次都會是新bean!
不加@Component
在匯入時要進行new ImportDataListener
!
小編這裡不想new了直接這樣寫!!如果不想這樣,可以使用構造器set進行使用!
BATCH_COUNT
:資料閾值,超過了就會清理list,在之前可以進行儲存到資料庫中,方便記憶體回收,防治OOM
!
這裡儲存到資料庫中一般使用批次儲存
,不要解析到一行就去儲存資料庫中,這樣資料量大會給資料庫增加IO,導致掛掉!這裡小編使用ServiceImpl
的saveBatch()
方法,也可以自己寫一下,像小編這樣寫,會出現迴圈依賴,加上@Lazy
就行!
/**
* @author wangzhenjun
* @date 2022/12/2 15:38
*/
@Slf4j
@Component
// 每次bean都是新的,不要單例
@Scope("prototype")
public class ImportDataListener implements ReadListener<Test> {
@Autowired
@Lazy
private TestService testService;
/**
* 每隔5條儲存資料庫,實際使用中可以100條,然後清理list ,方便記憶體回收
*/
private static final int BATCH_COUNT = 100;
/**
* 快取的資料
*/
private List<Test> importExcelDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 這個每一條資料解析都會來呼叫
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(Test data, AnalysisContext context) {
log.info("解析到一條資料:{}", JSON.toJSONString(data));
importExcelDataList.add(data);
// 達到BATCH_COUNT了,需要去儲存一次資料庫,防止資料幾萬條資料在記憶體,容易OOM
if (importExcelDataList.size() >= BATCH_COUNT) {
saveData();
// 儲存完成清理 list
importExcelDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有資料解析完成了 都會來呼叫
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 這裡也要儲存資料,確保最後遺留的資料也儲存到資料庫
saveData();
log.info("所有資料解析完成!");
}
/**
* 加上儲存資料庫
*/
private void saveData() {
log.info("{}條資料,開始儲存資料庫!", importExcelDataList.size());
testService.saveBatch(importExcelDataList);
log.info("儲存資料庫成功!");
}
}
五、Controller
/**
* @author wangzhenjun
* @date 2022/10/26 16:51
*/
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private TestService testService;
@PostMapping("/import")
public Result importExcel(@RequestBody MultipartFile multipartFile){
testService.importExcel(multipartFile);
return Result.success("ok");
}
}
六、Service
/**
* @author wangzhenjun
* @date 2022/10/26 16:55
*/
public interface TestService extends IService<Test> {
void importExcel(MultipartFile multipartFile);
}
七、ServiceImpl
/**
* @author wangzhenjun
* @date 2022/10/26 16:56
*/
@Service
public class TestServiceImpl extends ServiceImpl<TestDbMapper, Test> implements TestService{
@Autowired
private ImportDataListener importDataListener;
@SneakyThrows
@Override
public void importExcel(MultipartFile multipartFile) {
InputStream inputStream = multipartFile.getInputStream();
// 這裡 需要指定讀用哪個class去讀,然後讀取第一個sheet 檔案流會自動關閉
EasyExcel.read(inputStream, Test.class, importDataListener).sheet().doRead();
}
}
八、Mapper
/**
* @author wangzhenjun
* @date 2022/10/26 17:07
*/
public interface TestDbMapper extends BaseMapper<Test> {
}
九、測試
準備Excel資料:
postman上傳:
控制檯列印:
資料庫檢視:
完美搞定!!
十、總結
這樣就完成了easyExcel批次匯入Excel到資料庫,還是有很多要注意的點:
- 自定義轉換器
- 監聽器不要單例
- 儲存資料庫採用批次
- 版本差距
如果對你有幫助,還請不要吝嗇您的發財小手,你的一鍵三連是我寫作的動力,謝謝大家哈!!
可以看下小編的微信公眾號,文章首發看,歡迎關注,一起交流哈!!