SpringBoot查询数据库导出报表文件方式

 更新时间:2024年04月19日 15:36:12   作者:偷代码的猫  
这篇文章主要介绍了SpringBoot查询数据库导出报表文件方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

一、背景

1、需求

几千万条报表数据导出到Excel中

2、问题

在数据量导出不大时,我们的常规做法是使用MySQL直接查询出全部数据,整理规划成Excel列表,使用POI写入到Excel文件中

但是当数据量较大时,使用MySQL查询出所有数据,一会超时断开连接,二会内存溢出,使用POI暂时不支持分布写入数据到Excel中

3、解决

使用数据库流式读取可以解决数据库读取时间过长,内存溢出问题,这个解决了一次性读取全部数据到内存中

使用CSV文件代替xlsx/xls文件写入,CSV也可以使用Excel打开操作,并且也可另存为xlsx/xls,CSV本质是文本文件,不用确定尾结点,可以如TXT一样持续向文件追加内容

二、代码实现

1、mapper文件

使用Cursor游标标识,流式查询数据数据

  @Select("<script>"
      + "select * from `user_report`"
      + "</script>")
  Cursor<UserReport> selectCursorByCondition();

2、读取写入文件

  • @Transactional(readOnly = true):搭配数据库Cursor查询使用,事务注解为只读,保证数据整体的一致性
  • fieldArr:字段数组,规定要写入文件的字段项,比如查询的全部字段为a,b,c,d,而我只想显示a,c,d
  • headerArr:字段名数组,对应文件标题栏,需要不字段数组a,c,d名称一一对应
  • CsvUtils:一个简单的工具类,后续贴代码
/**
   * 导出Excel
   * @param fieldArr       字段数组
   * @param headerArr      字段名数组
   * @param fileName       文件名
   */
  @Transactional(readOnly = true)
  public void exportList(HttpServletResponse response,
      String[] fieldArr, String[] headerArr, String fileName) {
    //设置文件格式
    response.setCharacterEncoding("UTF-8");
    response.addHeader("Content-Type", "application/csv");
    try {
       //文件名 设置为中文
      fileName = new String(fileName.getBytes("gb2312"), "iso8859-1");
    } catch (Exception e) {
      log.error("file name show error:{}", e.getMessage());
    }
    //响应头部
    response.addHeader("Content-Disposition", "attachment; filename=" + fileName + "(" +
        DateUtil.format(DateUtil.date(), DatePattern.NORM_DATETIME_PATTERN) + ").csv");
    //查询数据
    Cursor<UserReport> modelStream = xxMapper.selectCursorByCondition();
 
    try {
      PrintWriter out = response.getWriter();
      try {
        //写入标题行
        out.write(CsvUtils.getTitleLine(headerArr));
        //写入数据行
        modelStream.forEach(item -> {
          out.write(CsvUtils.getRowLine(fieldArr, item));
        });
        out.flush();
      } catch (Exception e) {
        log.error("write file error:{}", e.getMessage());
      } finally {
        out.close();
      }
    } catch (Exception e) {
      log.error("exportList error:{}", e.getMessage());
    }
 
 
  }

3、CsvUtils工具类

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
 
/**
 * @Author: catcoder
 * @Desc:
 * @Time: 10:05 2021/8/2
 **/
public class CsvUtils {
 
  /**
   * CSV文件列分隔符
   */
  public static final String CSV_COLUMN_SEPARATOR = ",";
 
  /**
   * CSV文件行分隔符
   */
  public static final String CSV_ROW_SEPARATOR = System.lineSeparator();
 
 
  /**
   * 获取标题行
   *
   * @param headerArr 标题数组
   * @return
   */
  public static String getTitleLine(String[] headerArr) {
    StringBuffer line = new StringBuffer("");
    for (String title : headerArr) {
      line.append(title).append(CSV_COLUMN_SEPARATOR); //添加标题行数据
    }
    line.append(CSV_ROW_SEPARATOR); //换行数据
    return line.toString();
  }
 
  /**
   * 或去数据行
   *
   * @param fieldArr 字段数组
   * @param obj      实体对象
   * @return
   */
  public static String getRowLine(String[] fieldArr, Object obj) {
    StringBuffer line = new StringBuffer("");
    Class<?> srcClass = obj.getClass();
 
    //获取Obj 所有字段
    Set<String> objFiled = new HashSet<>();
    Field[] fields = obj.getClass().getDeclaredFields();
    for (Field field : fields) {
      objFiled.add(field.getName());
    }
 
    for (String field : fieldArr) {
      try {
        // 获取对象对应的Field
        if (objFiled.contains(field)) {
          Field objField = srcClass.getDeclaredField(field);
          objField.setAccessible(true); //设置private可访问
          Object value = objField.get(obj);
          line.append(value); //添加元素
        }
      } catch (Exception e) {
        e.printStackTrace();
      } finally {
        line.append(CSV_COLUMN_SEPARATOR); //CSV间隔数据
      }
    }
    line.append(CSV_ROW_SEPARATOR);//换行数据
    return line.toString();
  }
}

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Seata AT模式启动过程图文示例详解

    Seata AT模式启动过程图文示例详解

    这篇文章主要为大家介绍了Seata AT模式启动过程图文示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Java实现飞机大战游戏 附完整源码

    Java实现飞机大战游戏 附完整源码

    这篇文章主要介绍了Java实现飞机大战游戏,本文给大家分享完整源代码和效果图展示,对java飞机大战游戏实现代码感兴趣的朋友一起看看吧
    2022-05-05
  • Spring mvc整合mybatis(crud+分页插件)操作mysql

    Spring mvc整合mybatis(crud+分页插件)操作mysql

    这篇文章主要介绍了Spring mvc整合mybatis(crud+分页插件)操作mysql的步骤详解,需要的朋友可以参考下
    2017-04-04
  • Mybatis调用SQL Server存储过程的实现示例

    Mybatis调用SQL Server存储过程的实现示例

    在软件开发过程中,经常会使用到存储过程,本文就来介绍一下Mybatis调用SQL Server存储过程的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • springboot自定义Starter的具体流程

    springboot自定义Starter的具体流程

    这篇文章主要介绍了springboot自定义Starter的具体流程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • Java插入排序算法实现方法例子

    Java插入排序算法实现方法例子

    所谓排序,是将一组数据按照特定顺序重新排列的过程,稳定排序算法中相同键值的元素排序后保持原有顺序,直接插入排序和希尔排序是插入排序的两种形式,这篇文章主要介绍了Java插入排序算法实现的相关资料,需要的朋友可以参考下
    2024-10-10
  • Java反射与Fastjson的危险反序列化详解

    Java反射与Fastjson的危险反序列化详解

    在 Java 中,Computer.class是一个引用,它表示了 Computer 的字节码对象(Class对象),这个对象被广泛应用于反射、序列化等操作中,那么为什么 parseObject 需要这个引用呢,带着这个问题我们一起通过本文学习下吧
    2024-07-07
  • SpringBoot接口或方法进行失败重试的实现方式

    SpringBoot接口或方法进行失败重试的实现方式

    为了防止网络抖动,影响我们核心接口或方法的成功率,通常我们会对核心方法进行失败重试,如果我们自己通过for循环实现,会使代码显得比较臃肿,所以本文给大家介绍了SpringBoot接口或方法进行失败重试的实现方式,需要的朋友可以参考下
    2024-07-07
  • win7 64位系统JDK安装配置环境变量教程

    win7 64位系统JDK安装配置环境变量教程

    这篇文章主要为大家详细介绍了win7 64位系统JDK安装配置环境变量教程,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • java设计模式之外观模式学习笔记

    java设计模式之外观模式学习笔记

    这篇文章主要为大家详细介绍了java设计模式之外观模式学习笔记,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10

最新评论