使用eazyExcel讀取數據結合mybatis批量保存到數據庫(優化批量保存)


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層就自己寫了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM