1.开发背景

在web项目中,经常会需要查询数据导出excel,以前比较常见的就是用poi。使用poi的时候也有两种方式,一种就是直接将集合一次性导出为excel,还有一种是分批次追加的方式适合数据量较大的情况。poi支持xls和xlsx,使用2003版本的只支持6万多行以下的数据量,使用2007版本的支持百万行。但是呢,当数据量大了之后这种方式却非常耗内存和时间。

接触了etl之后就想着用kettle来做导数据,经过测试是完全可行的。几十万行,一百万行都能快速导出来,代码也非常简单。

 

2.kettle相关maven依赖如下

kettle 中的 MySQL 批量加载 kettle批量导入excel_后缀

kettle 中的 MySQL 批量加载 kettle批量导入excel_后缀_02

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>

kettle 中的 MySQL 批量加载 kettle批量导入excel_后缀_02

仓库如果没有kettle的jar包,可以先现在下来再上传到maven仓库

 

3.ktr文件:如以下附件下载链接

 由于不支持ktr路径的文件上传,所以我将它保存为xml文件,使用时将xml后缀去掉用ktr后缀就可以 了,该转换就是查询,导出为excel两个组件,如图所示:

kettle 中的 MySQL 批量加载 kettle批量导入excel_执行时间_04

这里用到一个输入和excel输出,里面配置的参数:

    查询语句: ${exec_select_sql}、

    文件名称:${filepath}、

    sheet名称:${sheetname}

 

4.调用ktr

kettle 中的 MySQL 批量加载 kettle批量导入excel_后缀_05

 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.打印执行信息,也可以直接在程序里面加

kettle 中的 MySQL 批量加载 kettle批量导入excel_后缀

kettle 中的 MySQL 批量加载 kettle批量导入excel_后缀_02

@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;
    }
}

kettle 中的 MySQL 批量加载 kettle批量导入excel_后缀_02

7.执行结果

* 导出10w行记录 

        执行时间: 1133ms

        执行时间: 1082ms 

        执行时间: 1096ms

* 导出100w行记录  

            执行时间: 39784ms

            执行时间: 8566ms 

            执行时间: 8622ms 
* Excel 2007行数极限 1048575 执行时间: 9686ms 

第一次导数据要加载kettle组件运行稍慢,后面几次再导数据速度就飞快了,更多结果有兴趣的可以去试试。