大量數據快速導出的解決方案-Kettle


1.開發背景
在web項目中,經常會需要查詢數據導出excel,以前比較常見的就是用poi。使用poi的時候也有兩種方式,一種就是直接將集合一次性導出為excel,還有一種是分批次追加的方式適合數據量較大的情況。poi支持xls和xlsx,使用2003版本的只支持6萬多行以下的數據量,使用2007版本的支持百萬行。但是呢,當數據量大了之后這種方式卻非常耗內存和時間。
接觸了etl之后就想着用kettle來做導數據,經過測試是完全可行的。幾十萬行,一百萬行都能快速導出來,代碼也非常簡單。
 
2.kettle相關maven依賴如下
 1 <dependency>
 2     <groupId>org.apache.commons</groupId>
 3     <artifactId>commons-vfs2</artifactId>
 4     <version>2.0</version>
 5 </dependency>
 6 <dependency>
 7     <groupId>org.scannotation</groupId>
 8     <artifactId>scannotation</artifactId>
 9     <version>1.0.3</version>
10 </dependency>
11 <dependency>
12     <groupId>dom4j</groupId>
13     <artifactId>dom4j</artifactId>
14     <version>1.6.1</version>
15 </dependency>
16 <dependency>
17     <groupId>pentaho-kettle</groupId>
18     <artifactId>kettle-vfs</artifactId>
19     <version>5.2.0.0</version>
20     <classifier>pentaho</classifier>
21 </dependency>
22 <dependency>
23     <groupId>pentaho-kettle</groupId>
24     <artifactId>kettle-engine</artifactId>
25     <version>5.2.0.0</version>
26 </dependency>
27 <dependency>
28     <groupId>pentaho-kettle</groupId>
29     <artifactId>kettle-core</artifactId>
30     <version>5.2.0.0</version>
31 </dependency>
Maven依賴

倉庫如果沒有kettle的jar包,可以先現在下來再上傳到maven倉庫

 

3.ktr文件:如以下附件下載鏈接

 由於博客園不支持ktr路徑的文件上傳,所以我將它保存為xml文件,使用時將xml后綴去掉用ktr后綴就可以 了,該轉換就是查詢,導出為excel兩個組件,如圖所示:
查詢數據導出為excel轉換

這里用到一個輸入和excel輸出,里面配置的參數:

    查詢語句: ${exec_select_sql}、

    文件名稱:${filepath}、

    sheet名稱:${sheetname}

 
4.調用ktr
 1 /** 
 2      * @功能描述: java調用Kettle導出的KTR,方法調用成功后,通過filepath參數獲取文件<br><font color="red">該程序已經指定數據源</font>
 3      * @創建作者: ***
 4      * @創建日期: 2016年11月1日 下午7:50:57
 5      * @param exec_select_sql:可執行的SELECT語句(案例:SELECT username '名稱',userName '員工名稱',ID 'ID' FROM `User`;)
 6      * @param filepath:保存的文件名稱,不含后綴,后綴統一xlsx(案例:C:\\test)
 7      * @param sheetname:文件中的sheet名稱(默認:下載)
 8      * @return
 9      */
10     public static boolean exportXlsx(String exec_select_sql, String filepath, String sheetname) {
11         if(StringUtils.isEmpty(exec_select_sql)||StringUtils.isEmpty(filepath))
12             return false;
13         Trans trans = null;
14         if(StringUtils.isEmpty(sheetname)) sheetname = "下載";
15         String uuid = UUID.randomUUID().toString();
16         logger_info.info("KettleUtil@exportXlsx:"+uuid+" {exec_select_sql:"+exec_select_sql+",filepath:"+filepath+",sheetname:"+sheetname+"}");
17         try {
18             String root_path = getPathMethod();
19             // 初始化
20             String fName = root_path+"export_xlsx.ktr";
21             // 轉換元對象
22             KettleEnvironment.init();// 初始化
23             EnvUtil.environmentInit();
24             TransMeta transMeta = new TransMeta(fName);
25             // 轉換
26             trans = new Trans(transMeta);
27             // 執行轉換
28             trans.setVariable("exec_select_sql", exec_select_sql);
29             trans.setVariable("filepath", filepath);
30             trans.setVariable("sheetname", sheetname);
31             trans.execute(null);
32             // 等待轉換執行結束
33             trans.waitUntilFinished();
34             // 拋出異常
35             if (trans.getErrors() > 0) {
36                 logger_info.info("KettleUtil@exportXlsx:"+uuid+" 執行失敗");
37             }else{
38                 logger_info.info("KettleUtil@exportXlsx:"+uuid+" 執行成功");
39             }
40             return true;
41         } catch (Exception e) {
42             logger_error.error("KettleUtil@exportXlsx:"+uuid, e);
43             return false;
44         }
45     }
46      
47     /** 
48      * @功能描述: 獲取編譯目錄
49      * @創建作者: ***
50      * @創建日期: 2016年11月1日 下午7:59:13
51      * @return
52      */
53     private static String getPathMethod(){ 
54         URL url= KettleUtil.class.getClassLoader().getResource(""); 
55         String p = url.getPath(); 
56         try { 
57             p=URLDecoder.decode(p, "UTF-8");
58         } catch (UnsupportedEncodingException e) {
59             logger_error.error("KettleUtil@getPathMethod:", e);
60         } 
61         return p; 
62     }
java調用kettle轉換

 

5.測試導出方法

web項目中的測試

@RequestMapping ( "/kettle" )
public  Object kettle( int  rows, String sql) {
     String sqlLimit = sql +  "LIMIT " +rows;
     String fullName = "/home/admin/DataPlatform/temp"+  "/kettle" +uuid;
     this .kettleExportExcel(sqlLimit, fullName,  "kettle" );
     return  null ;
}
也可以用main函數或junit測試
 
6.打印執行信息,也可以直接在程序里面加
@Component
@Aspect
public class ControllerAspect {
    private static Logger logger_info = Logger.getLogger("api-info");
    private static Logger logger_error = Logger.getLogger("api-error");
    /**
     * 切面
     */
    private final String POINT_CUT = "execution(* com.demo.controller.*.*(..))";
    @Pointcut(POINT_CUT)
    private void pointcut() {
    }
    @AfterThrowing(value = POINT_CUT, throwing = "e")
    public void afterThrowing(Throwable e) {
        logger_error.error("afterThrowing: " + e.getMessage(), e);
    }
    /**
     * @功能描述: 打印Controller方法的執行時間
     * @創建日期: 2016年11月2日 上午11:44:11
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = POINT_CUT)
    public Object around(ProceedingJoinPoint proceedingJoinPoint)
            throws Throwable {
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        String methodName = proceedingJoinPoint.getSignature().getName();
        Long begin = System.currentTimeMillis();
        Long beginMemory = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
        StringBuilder log = new StringBuilder(className+"@"+methodName);
        Object result = null;
        try {
            result = proceedingJoinPoint.proceed();
        } catch (Exception e) {
            logger_error.error(log + e.getMessage(), e);
        }
        Long end = System.currentTimeMillis();
        Long endMemory = Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
        log.append(" 執行時間: ").append(end - begin).append("ms");
        log.append(" 消耗內存: ").append(endMemory - beginMemory).append("Byte");
        logger_info.info(log);
        return result;
    }
}
View Code

7.執行結果

* 導出10w行記錄 

        執行時間: 1133ms

        執行時間: 1082ms 

        執行時間: 1096ms


* 導出100w行記錄  

            執行時間: 39784ms

            執行時間: 8566ms 

            執行時間: 8622ms 
* Excel 2007行數極限 1048575 執行時間: 9686ms 

第一次導數據要加載kettle組件運行稍慢,后面幾次再導數據速度就飛快了,更多結果有興趣的可以去試試。

 

僅供參考,不足之處還請見諒,歡迎指正!轉載請標明出處。如有疑問,歡迎評論或者聯系我郵箱1034570286@qq.com

 


免責聲明!

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



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