Java导出数据库查询结果为excel和csv的简易实现

转载请注明来源-作者@loongshawn:http://blog.csdn.net/loongshawn/article/details/53457953,建议读者阅读原文,确保获得完整的信息

1.背景

       最近在应对一个数据查询导出模块,总体要求就是依据给定的SQL语句,输出其查询结果为csv或者xlsx文件。其中查询数据量可能会有大数据量,成百上千万都可能。

2.探讨

       针对上述数据导出这个问题,提取出几个关键词:

  • 1、SQL是由作业人员临时写的。
  • 2、数据量大。
  • 3、输出csv或xlsx文件。

       针对这几个关键词,咱分别扩展下其含义:

       第一个关键词:SQL是临时写的,这就意味着咱只是去执行这条语句,并不能对语句进行分页设计啥的,如果要重新分析SQL可能比较困难。这个时候比较贴近的场景就是“数据库查询客户端”,客户端只管执行SQL,至于执行得快慢等取决于语句及数据库性能等。

       第二个关键词:数据量大,即查询的返回结果可能比较多,你如何处理返回结果,是将其先存到List列表还是直接在结果集里面就给输出到文件。这就需要考虑内存、机器性能问题,不要一条语句执行了,直接导致你的java程序死掉了,比如JVM内存溢出,CPU使用率蹭蹭的涨到99%,导致整个程序无响应。

       第三个关键词:输出csv或xlsx文件,比如csv是利用成熟的三方库还是自己写(毕竟就是逗号分隔的文本),不同人可能有不同看法,但是我主张大家用现成的三方依赖包,比如javacsv\opencsv都是比较成熟的工具包。

       其中,有关csv读写在文章《 利用JavaCSV API来读写csv文件》中有详细介绍。而有关xlsx读写需要的jar包则在前面的文章《 Java处理excel两种不同的方式》有过介绍。

3.实现

       在实例“Java导出数据库查询结果”中,我选取的实现方法为直接在ResultSet结果集中将数据写入到文件,这么操作基于两点:

  • 1、做分页困难,没法降低查询数据量。
  • 2、大数据量内存稀缺,尽量减少重复数据存储。

3.1导出csv

       使用的依赖库:

<!-- https://mvnrepository.com/artifact/net.sourceforge.javacsv/javacsv -->
<dependency>
    <groupId>net.sourceforge.javacsv</groupId>
    <artifactId>javacsv</artifactId>
    <version>2.1</version>
</dependency>
 
 

           截取部分代码片段

    // 判断文件是否存在,存在则删除,然后创建新表格
    File tmp = new File(filePath);
    if (tmp.exists()){
    
        if (tmp.delete()){
            logger.info(filePath + Constant.DUPLICATE_FILE_DELETE);
        }
    }
    
    // 创建CSV写对象
    CsvWriter csvWriter = new CsvWriter(filePath,Constant.SEPARATOR, Charset.forName("GBK"));
    
    // 数据查询开始
    preparedStatement = connection.prepareStatement(sql);
    resultSet = preparedStatement.executeQuery();
    
    // 获取结果集表头
    ResultSetMetaData md = resultSet.getMetaData();
    int columnCount = md.getColumnCount();
    logger.debug("返回结果字段个数:" + columnCount);
    
    JSONArray columnName = new JSONArray();
    for (int i = 1; i <= columnCount; i++) {
    JSONObject object = new JSONObject();
    object.put(KEY.COLUNM_NAME,md.getColumnName(i));
    columnName.add(object);
    }
    
    // 获取表头数组
    int columnSize = columnName.size();
    String[] columnNameList = ListUtil.getListFromJSONArray(columnName);
    csvWriter.writeRecord(columnNameList);
    
    // 数据记录数
    int i = 0;
    // 临时数据存储
    StringBuffer stringBuffer = new StringBuffer();
    
    while (resultSet.next()) {
    
        // 记录号
        i++;
        // 依据列名获取各列值
        for (int j = 1; j<=columnSize; j++){
    
            String value = resultSet.getString(j);
            //创建列
            stringBuffer.append(value);
            if (j != columnSize){
                stringBuffer.append(Constant.COMMA);
            }
        }
    
        String buffer_string = stringBuffer.toString();
        String[] content = buffer_string.split(Constant.COMMA);
        csvWriter.writeRecord(content);
        stringBuffer.setLength(0);
    }
    
    // 文件输出
    csvWriter.flush();
    csvWriter.close();
     
     

             针对相同文件导出csv结果如下:

      这里写图片描述

      3.2导出xlsx

             使用的依赖库:

      <!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
      <dependency>
          <groupId>org.apache.poi</groupId>
          <artifactId>poi</artifactId>
          <version>3.15</version>
      </dependency>
      
      <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
      <dependency>
          <groupId>org.apache.poi</groupId>
          <artifactId>poi-ooxml</artifactId>
          <version>3.15</version>
      </dependency>
      
      <!-- https://mvnrepository.com/artifact/org.apache.xmlbeans/xmlbeans -->
      <dependency>
          <groupId>org.apache.xmlbeans</groupId>
          <artifactId>xmlbeans</artifactId>
          <version>2.6.0</version>
      </dependency>
      
      <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml-schemas -->
      <dependency>
          <groupId>org.apache.poi</groupId>
          <artifactId>poi-ooxml-schemas</artifactId>
          <version>3.14</version>
      </dependency>
       
       

               截取部分代码片段:

        // 数据查询开始
        preparedStatement = connection.prepareStatement(sql);
        resultSet = preparedStatement.executeQuery();
        
        // 获取结果集表头
        ResultSetMetaData md = resultSet.getMetaData();
        int columnCount = md.getColumnCount();
        logger.debug("返回结果字段个数:" + columnCount);
        
        JSONArray columnName = new JSONArray();
        for (int i = 1; i <= columnCount; i++) {
            JSONObject object = new JSONObject();
            object.put(KEY.COLUNM_NAME,md.getColumnName(i));
            columnName.add(object);
        }
        int columnNameSize = columnName.size();
        
        //ExcelUtil.createSXSSFFile(columnName,filePath);
        //inputFS = new FileInputStream(tmp);
        
        // 读取工作薄
        Workbook wb = new SXSSFWorkbook(500);
        // 记录总数
        int i = 0;
        
        while (resultSet.next()) {
        
            // 记录号
            i++;
            // logger.info("i:"+i);
        
            int factor = CalculateUtil.getMultiplyingFactor(i);
            int index = i%Constant.XLSX_LENGTH;
            // logger.info("index:"+index);
        
            if ( index == 1){
                // 创建工作表
                sheet = wb.createSheet("sheet"+factor);
                // 写表头
                ExcelUtil.writeTitle(sheet,columnName);
            }
            // 创建行
            Row row;
            if ( index != 0) {
                row = sheet.createRow(index);
            } else {
                row = sheet.createRow(Constant.XLSX_LENGTH);
            }
            // 依据列名获取各列值
            for (int j = 0; j < columnNameSize; j++) {
        
                String value = resultSet.getString(j + 1);
                //创建列
                Cell cell = row.createCell((short) j);
                cell.setCellValue(value);
            }
        }
        
        // 文件输出
        FileOutputStream out = new FileOutputStream(filePath);
        wb.write(out);
        out.flush();
        out.close();
         
         

                 在导出xlsx里面需要补充说明一点的就是SXSSFWorkbook这个对象的使用,如下:

          Workbook wb = new SXSSFWorkbook(500);
           
           
          • 1

                 因为通常咱使用习惯都是利用XSSFWorkbook来创建xlsx,两者明显的区别就是,SXSSFWorkbook可以设定内存数据写入硬盘的阈值,即每提交多少条数据就写入一次硬盘,有效的避免了大数据量存储时内存溢出的风险。比如代码中我指定的阈值为500条。

                 有关SXSSFWorkbook对象的详细说明可以参考以下内容:

          这里写图片描述

                 从上图中可以看出,SXSSF是对XSSF的扩展,用来应对大容量电子表格的输出,自3.8-beta3版本的poi库就添加了。

                 针对相同文件导出excel结果如下:

          这里写图片描述

                 补充说明:

          excel存储容量,2010版后支持单张sheet表格最大行数1048576,sheet表最大数目没有统一说法,但有一点可以肯定,数据量大太加上自己电脑的性能的限制,excel整体性能会受影响,不是说无限制往里存。

          4.总结

          csv与excel数据存储能力及效率对比:

          属性csvexcel
          存储容量没有限制,类似txt文本单张sheet表有限制,可以存大量sheet表
          存储效率相对csv慢
          占用空间相同内容,少量时占用空间少,大量时占用空间大相同内容,少量占用空间大,大量占用空间少

                 其中相同文件名均代表同一SQL输出不同格式的文件,从下图中可以看出,相同的查询结果,如果内容较少(几十KB),存放在csv文件中占用空间较少。如果内容较大(几百KB),存放在excel文件中占用空间较少,这个应该是excel文件在大容量时做了性能优化。毕竟MicroSoft是靠系统和办公软件起家,其excel还是具备含金量的,不仅仅是好看,在高级层面还是做了不少工作的。

                 输出csv及excel文件结果对比图:

          这里写图片描述

          5.交流

                 最近有同学说需要完整的代码,限于公司规定,完整代码不能提供。但是如果有同学在实际操作过程中碰到问题、不理解之处都可以通过留言指出来,看看可否协助你分析下。

          • 0
            点赞
          • 2
            收藏
            觉得还不错? 一键收藏
          • 0
            评论
          评论
          添加红包

          请填写红包祝福语或标题

          红包个数最小为10个

          红包金额最低5元

          当前余额3.43前往充值 >
          需支付:10.00
          成就一亿技术人!
          领取后你会自动成为博主和红包主的粉丝 规则
          hope_wisdom
          发出的红包
          实付
          使用余额支付
          点击重新获取
          扫码支付
          钱包余额 0

          抵扣说明:

          1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
          2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

          余额充值