最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Java實現(xiàn)百萬Excel數(shù)據(jù)導(dǎo)出

 更新時間:2024年03月07日 09:54:33   作者:夢塵啊  
這篇文章主要為大家詳細介紹了如何使用Java實現(xiàn)百萬Excel數(shù)據(jù)導(dǎo)出,文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以參考一下

可能出現(xiàn)的問題

  • 同步導(dǎo)數(shù)據(jù),接口很容易超時。
  • 如果把所有數(shù)據(jù)一次性裝載到內(nèi)存,很容易引起OOM。
  • 數(shù)據(jù)量太大sql語句慢。
  • 如果走異步,如何通知用戶導(dǎo)出結(jié)果
  • 如果excel文件太大,目標用戶打不開怎么辦

解決方案

問題一 異步化 調(diào)用接口立即返回任務(wù)生產(chǎn)成功

問題二 分批查詢 poi 禁止使用XSSFWorkbook 使用SXSSFWorkbook 或 easy Excel

問題三 分頁通過滾動翻頁查詢

流式查詢問題:容易長時間占用數(shù)據(jù)庫鏈接池資源。

游標查詢問題:應(yīng)用指定每次查詢獲取的條數(shù)fetchSize,MySQL服務(wù)器每次只查詢指定條數(shù)的數(shù)據(jù),由于MySQL方不知道客戶端什么時候?qū)?shù)據(jù)消費完,MySQL需要建立一個臨時空間來存放每次查詢出的數(shù)據(jù),大數(shù)據(jù)量時MySQL服務(wù)器、磁盤占用都會飆升。

故使用滾動翻頁查詢

問題四 通過 頁面或者溝通軟件通知用戶導(dǎo)出成功 ,并將導(dǎo)出結(jié)果上傳至oss 后續(xù)可直接下載 無需重復(fù)導(dǎo)出

問題五 導(dǎo)出可用戶設(shè)置最大條數(shù)

數(shù)據(jù)準備

CREATE TABLE `t_order`
(
    `id`           BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主鍵',
    `creator`      VARCHAR(16)     NOT NULL DEFAULT 'admin' COMMENT '創(chuàng)建人',
    `editor`       VARCHAR(16)     NOT NULL DEFAULT 'admin' COMMENT '修改人',
    `create_time`  DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時間',
    `edit_time`    DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時間',
    `version`      BIGINT          NOT NULL DEFAULT 1 COMMENT '版本號',
    `deleted`      TINYINT         NOT NULL DEFAULT 0 COMMENT '軟刪除標識',
    `order_id`     VARCHAR(32)     NOT NULL COMMENT '訂單ID',
    `amount`       DECIMAL(10, 2)  NOT NULL DEFAULT 0 COMMENT '訂單金額',
    `payment_time` DATETIME        NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT '支付時間',
    `order_status` TINYINT         NOT NULL DEFAULT 0 COMMENT '訂單狀態(tài),0:處理中,1:支付成功,2:支付失敗',
    UNIQUE uniq_order_id (`order_id`),
    INDEX idx_payment_time (`payment_time`)
) COMMENT '訂單表';
public class OrderServiceTest {
    private static final Random OR = new Random();
    private static final Random AR = new Random();
    private static final Random DR = new Random();
    @Test
    public void testGenerateTestOrderSql() throws Exception {
        HikariConfig config = new HikariConfig();
        config.setUsername("root");
        config.setPassword("root");
        config.setJdbcUrl("jdbc:mysql://localhost:3306/local?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false");
        config.setDriverClassName("com.mysql.jdbc.Driver");
        HikariDataSource hikariDataSource = new HikariDataSource(config);
        JdbcTemplate jdbcTemplate = new JdbcTemplate(hikariDataSource);
        for (int d = 0; d < 100; d++) {
            String item = "('%s','%d','2020-07-%d 00:00:00','%d')";
            StringBuilder sql = new StringBuilder("INSERT INTO t_order(order_id,amount,payment_time,order_status) VALUES ");
            for (int i = 0; i < 20_000; i++) {
                sql.append(String.format(item, UUID.randomUUID().toString().replace("-", ""),
                        AR.nextInt(100000) + 1, DR.nextInt(31) + 1, OR.nextInt(3))).append(",");
            }
            jdbcTemplate.update(sql.substring(0, sql.lastIndexOf(",")));
        }
        hikariDataSource.close();
    }
}

具體實現(xiàn)

easy Excel通過滾動翻頁

Controller

@GetMapping(path = "/export")
public void export(@RequestParam(name = "paymentDateStart") String paymentDateStart,
                   @RequestParam(name = "paymentDateEnd") String paymentDateEnd,
                   ) throws Exception {
    orderService.export(paymentDateStart, paymentDateEnd);
}

Service

@Async
public void export(String paymentDateStart, String paymentDateEnd) throws IOException {
    Date dateBefore = new Date();
    String fileName = URLEncoder.encode(String.format("%s-(%s).xlsx", "訂單數(shù)據(jù)", UUID.randomUUID()),
            StandardCharsets.UTF_8.toString());
    BufferedOutputStream outputStream = FileUtil.getOutputStream(FileUtil.file("/Users/Documents/github/"+fileName));

    ExcelWriter writer = new ExcelWriterBuilder()
            .autoCloseStream(true)
            .excelType(ExcelTypeEnum.XLSX)
            .file(outputStream)
            .head(OrderDTO.class)
            .build();
    WriteSheet writeSheet = new WriteSheet();
    writeSheet.setSheetName("target");
    long lastBatchMaxId = 0L;
    int limit = 3000;
    for (; ; ) {
        List<OrderDTO> list = queryByScrollingPagination(paymentDateTimeStart, paymentDateTimeEnd, lastBatchMaxId, limit);
      //可以添加導(dǎo)出條數(shù)限制
        if (list.isEmpty()) {
            writer.finish();
            Date dateAfter = new Date();
            System.out.println("導(dǎo)出列表共執(zhí)行" + (dateAfter.getTime() - dateBefore.getTime()) + "ms");
            //todo 上傳oss 發(fā)通知
            break;
        } else {
            lastBatchMaxId = list.stream().map(OrderDTO::getId).max(Long::compareTo).orElse(Long.MAX_VALUE);
            writer.write(list, writeSheet);
        }
    }
}
public List<OrderDTO> queryByScrollingPagination(String paymentDateTimeStart,
                                                 String paymentDateTimeEnd,
                                                 long lastBatchMaxId,
                                                 int limit) {
    LocalDateTime start = LocalDateTime.parse(paymentDateTimeStart, formatter);
    LocalDateTime end = LocalDateTime.parse(paymentDateTimeEnd, formatter);
    return orderDao.queryByScrollingPagination(lastBatchMaxId, limit, start, end).stream().map(order -> {
        OrderDTO dto = new OrderDTO();
        dto.setId(order.getId());
        dto.setAmount(order.getAmount());
        dto.setOrderId(order.getOrderId());
        dto.setCreator(order.getCreator());
        return dto;
    }).collect(Collectors.toList());
}

Repository

@Repository
public class OrderDao {

    @Resource
    private  JdbcTemplate jdbcTemplate;

    public List<Order> queryByScrollingPagination(long lastBatchMaxId,
                                                  int limit,
                                                  LocalDateTime paymentDateTimeStart,
                                                  LocalDateTime paymentDateTimeEnd) {
        return jdbcTemplate.query("SELECT id,creator,editor ,version,deleted,order_id,amount,order_status FROM t_order WHERE id > ? AND payment_time >= ? AND payment_time <= ? " +
                        "ORDER BY id ASC LIMIT ?",
                p -> {
                    p.setLong(1, lastBatchMaxId);
                    p.setTimestamp(2, Timestamp.valueOf(paymentDateTimeStart));
                    p.setTimestamp(3, Timestamp.valueOf(paymentDateTimeEnd));
                    p.setInt(4, limit);
                },
                rs -> {
                    List<Order> orders = new ArrayList<>();
                    while (rs.next()) {
                        Order order = new Order();
                        order.setId(rs.getLong("id"));
                        order.setCreator(rs.getString("creator"));
                        order.setEditor(rs.getString("editor"));
                           order.setVersion(rs.getLong("version"));
                        order.setDeleted(rs.getInt("deleted"));
                        order.setOrderId(rs.getString("order_id"));
                        order.setAmount(rs.getBigDecimal("amount"));
                       order.setOrderStatus(rs.getInt("order_status"));
                        orders.add(order);
                    }
                    return orders;
                });
    }
}

總結(jié)

業(yè)務(wù)方面

做需求時刻先考慮是不是必須要做 、如果必須要做的情況需要考慮用戶的體驗和使用感受

技術(shù)方面

1 不需要立馬返回結(jié)果的接口可以采用異步的方式讓接口立刻返回結(jié)果,可以防止接口耗時過長導(dǎo)致tomcat線程池打滿。

2 MySQL批量查詢、數(shù)據(jù)同步、數(shù)據(jù)導(dǎo)出可以使用類似于分頁查詢的思路,但是鑒于LIMIT offset,size的效率太低,可以采用”滾動翻頁”的實現(xiàn)方式 注意要用自增趨勢的主鍵

3 數(shù)據(jù)導(dǎo)出需要注意由于大對象頻繁創(chuàng)建導(dǎo)致的 full gc 和oom 如果導(dǎo)出較頻繁可以考慮拆分單獨服務(wù)專門做導(dǎo)出

到此這篇關(guān)于使用Java實現(xiàn)百萬Excel數(shù)據(jù)導(dǎo)出的文章就介紹到這了,更多相關(guān)Java數(shù)據(jù)導(dǎo)出內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mybatis中的Criteria條件查詢方式

    Mybatis中的Criteria條件查詢方式

    這篇文章主要介紹了Mybatis中的Criteria條件查詢方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 如何使用Maven創(chuàng)建web目錄結(jié)構(gòu)

    如何使用Maven創(chuàng)建web目錄結(jié)構(gòu)

    這篇文章主要介紹了如何使用Maven創(chuàng)建web目錄結(jié)構(gòu)的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • IDEA一鍵完成格式化、去除無用引用、編譯的操作

    IDEA一鍵完成格式化、去除無用引用、編譯的操作

    這篇文章主要介紹了IDEA一鍵完成格式化、去除無用引用、編譯的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Java中文件管理系統(tǒng)FastDFS詳解

    Java中文件管理系統(tǒng)FastDFS詳解

    這篇文章主要介紹了Java中文件管理系統(tǒng)FastDFS詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java異常報錯:java.nio.file.FileSystemException的多種解決方案

    Java異常報錯:java.nio.file.FileSystemException的多種解決方案

    在Java應(yīng)用程序中處理文件和目錄時,java.nio.file.FileSystemException是一個常見的異常,這個異常發(fā)生在嘗試進行文件系統(tǒng)操作時,本文將詳細探討FileSystemException的成因,并提供多種解決方案,需要的朋友可以參考下
    2024-12-12
  • Java多線程實現(xiàn)的兩種方式

    Java多線程實現(xiàn)的兩種方式

    本文主要介紹了Java多線程實現(xiàn)的兩種方式:繼承Thread類、實現(xiàn)Runnable接口。具有一定的參考價值,下面跟著小編一起來看下吧
    2017-01-01
  • SpringBoot配置Redis實現(xiàn)保存獲取和刪除數(shù)據(jù)

    SpringBoot配置Redis實現(xiàn)保存獲取和刪除數(shù)據(jù)

    本文主要介紹了SpringBoot配置Redis實現(xiàn)保存獲取和刪除數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Java學(xué)習(xí)教程之定時任務(wù)全家桶

    Java學(xué)習(xí)教程之定時任務(wù)全家桶

    這篇文章主要給大家介紹了關(guān)于Java學(xué)習(xí)教程之定時任務(wù)全家桶的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • java內(nèi)部類之成員內(nèi)部類、局部內(nèi)部類和匿名內(nèi)部類用法及說明

    java內(nèi)部類之成員內(nèi)部類、局部內(nèi)部類和匿名內(nèi)部類用法及說明

    這篇文章主要介紹了java內(nèi)部類之成員內(nèi)部類、局部內(nèi)部類和匿名內(nèi)部類的用法及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Java ThreadLocal的設(shè)計理念與作用

    Java ThreadLocal的設(shè)計理念與作用

    這篇文章主要介紹了Java ThreadLocal的設(shè)計理念與作用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03

最新評論

深水埗区| 盐源县| 巴楚县| 广安市| 布尔津县| 临清市| 杭锦旗| 南川市| 白山市| 阳高县| 建阳市| 崇仁县| 秦皇岛市| 平顶山市| 宁德市| 余江县| 大同市| 繁峙县| 丰宁| 绍兴县| 禄丰县| 新巴尔虎左旗| 菏泽市| 湘阴县| 樟树市| 咸丰县| 盐池县| 同江市| 龙里县| 竹北市| 武陟县| 娄烦县| 彰化县| 南昌县| 调兵山市| 三河市| 余干县| 时尚| 汉源县| 榆树市| 凤城市|