easy-excel (mybatis oracle 批量插入sql優化)
數據量: 5萬6 , 每行 30多個字段
執行平均用時: 30秒(看電腦配置)
准備依賴(2.1.1版本要求POI的版本必須為4.0及以上)
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.1</version> </dependency>
EazyExcelUtil 類
public class EazyExcelUtil { /** * 從Excel中讀取文件,讀取的文件是一個DTO類,該類必須繼承BaseRowModel * 具體實例參考 : MemberMarketDto.java * 參考:https://github.com/alibaba/easyexcel * 字符流必須支持標記,FileInputStream 不支持標記,可以使用BufferedInputStream 代替 * BufferedInputStream bis = new BufferedInputStream(new FileInputStream(...)); */ public static <T extends BaseRowModel> List<T> readExcel(final InputStream inputStream, final Class<? extends BaseRowModel> clazz) { if (null == inputStream) { throw new NullPointerException("the inputStream is null!"); } ExcelListener<T> listener = new ExcelListener<>(); // 這里因為EasyExcel-1.1.1版本的bug,所以需要選用下面這個標記已經過期的版本 ExcelReader reader = new ExcelReader(inputStream, valueOf(inputStream), null, listener); reader.read(new com.alibaba.excel.metadata.Sheet(1, 1, clazz)); reader.finish(); return listener.getRows(); } public static void writeExcel(final File file, List<? extends BaseRowModel> list) { try (OutputStream out = new FileOutputStream(file)) { ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX); //寫第一個sheet, 有模型映射關系 Class<? extends BaseRowModel> t = list.get(0).getClass(); Sheet sheet = new Sheet(1, 0, t); writer.write(list, sheet); writer.finish(); } catch (IOException e) { // log.warn("fail to write to excel file: file[{}]", file.getName(), e); throw new SysException("寫入excel 異常"); } } /** * 根據輸入流,判斷為xls還是xlsx,該方法原本存在於easyexcel 1.1.0 的ExcelTypeEnum中。 */ public static ExcelTypeEnum valueOf(InputStream inputStream) { try { FileMagic fileMagic = FileMagic.valueOf(inputStream); if (FileMagic.OLE2.equals(fileMagic)) { return ExcelTypeEnum.XLS; } if (FileMagic.OOXML.equals(fileMagic)) { return ExcelTypeEnum.XLSX; } throw new IllegalArgumentException("excelTypeEnum can not null"); } catch (IOException e) { throw new RuntimeException(e); } } }
ExcelListener 監聽器
public class ExcelListener <T extends BaseRowModel> extends AnalysisEventListener<T> { private final List<T> rows = new ArrayList<>(); @Override public void invoke(T t, AnalysisContext analysisContext) { rows.add(t); } @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { } public List<T> getRows() { return rows; } }
DTO實體對應excel
public class Demo extends BaseRowModel { //需要繼承BaseRowModel @ExcelProperty(index = 0) //指定字段對應excel的某列 private String name;//get...set... }
Dao層
public interfase DemoDao { //這里只是模擬 void save(@Param("list") List<Demo> list); }
mapper
<insert id="save"> insert into demo (name) <foreach close=")" collection="list" item="item" index="index" open="(" separator="union"> select #{item.name} from dual </foreach> </insert>
service
@Service
@Transactional
public class DemoService{
public void save(MultipartFile file, String year) {
int divNum = 50; //分組保存數量(有foreach的情況下20到50條的性能最好),否則使用單條保存 List<ZfZhiFuLingData> list = null; //讀取excel數據 try { fis = file.getInputStream(); list = EazyExcelUtil.readExcel(new BufferedInputStream(fis), Demo.class); } catch (IOException e) { throw new SysException("文件數據不符合規定,請重新上傳" + e.getMessage()); } finally { if (null != fis) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } // SqlSession session = sessionFactory.openSession(ExecutorType.BATCH); DemoDao mapper = session.getMapper(DemoDao.class); if (list.size() > 0) { //為了防止SQL語句超出長度出錯,分成幾次插入 if (list.size() <= divNum) { mapper.save(list); } else { int length = list.size(); // 計算可以分成多少組 int num = (length + divNum - 1) / divNum; for (int i = 0; i < num; i++) { // 開始位置 int fromIndex = i * divNum; // 結束位置 int toIndex = (i + 1) * divNum < length ? (i + 1) * divNum : length; mapper.save(list.subList(fromIndex, toIndex)); } } session.flushStatements(); } }
}
}
Controller層就自己寫了