用JDBC游标的方式导出mysql数据以及springboot打包成exe程序实践

用JDBC实现游标查询,关键代码在于 Statement 的 fetchSize 属性的设置。

  • ExportDataService
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.text.csv.CsvWriter;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.File;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

@Slf4j
@Service
@RequiredArgsConstructor
public class ExportDataService {

    private final JdbcTemplate jdbcTemplate;

    @Async
    public void exportDataToFile(String sql, String filePath) {
        long beginTime = System.currentTimeMillis();
        // 给文件先加个.ing后缀, 表示数据正在导出
        File file = FileUtil.newFile(filePath + ".ing");
        AtomicInteger readCnt = new AtomicInteger();
        try (CsvWriter csvWriter = CsvUtil.getWriter(file, Charset.defaultCharset())) {
            jdbcTemplate.query(conn -> buildStmt(conn, sql), rs -> {
                handlerRow(rs, csvWriter, readCnt);
            });
            csvWriter.flush();
        }
        log.info("导出完成, 共计: {}, 耗时: {} MS", readCnt.get(), System.currentTimeMillis() - beginTime);
        // 全部导出完成后, 重命名文件, 把.ing后缀去掉
        FileUtil.rename(file, StrUtil.subBefore(file.getName(), ".ing", true), true);
    }
    
    /**
     * 构建预执行SQL模板
     *
     * @param conn 数据连接
     * @param sql  SQL
     * @return java.sql.PreparedStatement
     * @author 敖癸
     * @since 2024/3/15 - 15:37
     */
    private static PreparedStatement buildStmt(Connection conn, String sql) throws SQLException {
        PreparedStatement stmt = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
        // 固定写法, 因为mysql比较特殊, 要开启游标查询, 需要设置fetchSize为Integer.MIN_VALUE。其它数据库根据实际需求来调整大小
        stmt.setFetchSize(Integer.MIN_VALUE);
        stmt.setFetchDirection(ResultSet.FETCH_FORWARD);
        return stmt;
    }

    /** 处理每一行的数据 */
    private static void handlerRow(ResultSet rs, CsvWriter csvWriter, AtomicInteger readCnt) throws SQLException {
        // 获取每一行的字段数量
        int columnCount = rs.getMetaData().getColumnCount();
        // 按顺序获取每个字段的值
        String[] fields = IntStream.rangeClosed(1, columnCount).mapToObj(i -> getFieldValue(rs, i)).toArray(String[]::new);
        // 将一行数据append写入到csv文件
        csvWriter.writeLine(fields);
        // 每5000行清空一次缓冲区, 将数据持久化到磁盘
        if (readCnt.incrementAndGet() % 5000 == 0) {
            csvWriter.flush();
            log.info("已导出: {}", readCnt.get());
        }
    }

    private static String getFieldValue(ResultSet rs, int i) {
        try {
            return rs.getString(i);
        } catch (SQLException e) {
            log.warn("字段解析失败", e);
            return "N/A";
        }
    }
}

项目启动后,发起请求
在这里插入图片描述
源码: https://gitee.com/DimonHo/export-data2csv.git

扩展知识

因为是直接导出数据写入到本地文件,所以这个程序不能部署到服务器,如果其它的小伙伴也想要用这个功能怎么办?
方案1. 要么修改一下代码,导出后先把文件写入到服务器,然后再下载到客户端,这样就会多走一层网络传输,如果导出的数据很大,到时候要从服务器上下载几个G的文件,对网络带宽和效率都是一种消耗。
方案2. 把项目打包成jar包,再把jar分发给需要的人,让它们自己在本机启动java,这种方式需要使用者的电脑上安装jre运行环境,如果是非技术人员,使用门槛较高。
方案3. 把项目打包成exe可执行文件,双击即可使用。

前两个方案就不说了,比较简单,大家应该都会。第三种方案怎么玩?
百度一下通常有两种方案,针对jdk14之前和之后分别有一个不同的解决方案。
jdk14之前,你可能需要用到 exe4j 这个工具,这个我没试过,看了下别人写的教程,感觉步骤有点麻烦,就不赘叙了,毕竟现在已经有更简单的办法了。
jdk14之后,jdk提供了一个jpackage命令工具,用这个工具,我们就可以用简单的几个命令直接打包成绿色版exe程序。下面来看看具体怎么玩。

  1. 首先,我们仍然需要先把源码编译成jar包,用 mvn package命令编译打包即可。
  2. 然后在执行jpackage命令把jar打包成exe程序
jpackage --type app-image --name exportData2Csv --input target --main-jar .\export-data2csv.jar --win-console --dest D:/apps

–type可选值有:app-image | exe | msi,这里我们使用app-image即可
–name:exe文件的名称
–input:输入目录,写jar包所在的目录即可
–main-jar: 指定主类的jar包
–dest: 打包后的目的地

运行完成后,在D:/apps目录下面就会多出一个exportData2Csv目录,进入这个目录,就能看到exportData2Csv.exe文件了
在这里插入图片描述
注意:exportData2Csv.exe不可单独拿出来运行,这里其实是基本把整个jre打包进来了,在runtime目录下。
我们可以把D:/apps/exportData2Csv整个目录压缩到一个zip包里面,就可以把这个绿色版的数据导出程序分发给别人了。

要做的更好用点,就在打包前在exportData2Csv目录下面在加一个readme.md文件,里面简单的写上使用教程,比如数据源配置如何修改。
说到这里,数据源的配置也有两种方式可以修改。
一种是运行这个exe文件时,指定参数文件路径 --spring.config.location=./application.yml
就像这样:
在这里插入图片描述

另一种方法是jpackage打包时,指定--arguments --spring.config.location=./application.yml参数,打包完成后,再手动把application.yml复制到exportData2Csv目录。
就像这样:
在这里插入图片描述
再添加readme.md说明文件
在这里插入图片描述
添加说明
在这里插入图片描述
最后打包成压缩包,把zip分发给别人使用
在这里插入图片描述

tips: 程序启动后,自动打开swagger ui页面,可以在main方法中加入这段代码

        try {
            // 使用默认浏览器打开swagger ui页面
            new ProcessBuilder("cmd", "/c", "start", "http://localhost:8080/swagger-ui.html").start();
        } catch (Exception e) {
            log.warn("打开客户端主页失败", e);
        }

在这里插入图片描述
在这里插入图片描述
是不是更好用了?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/478157.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

优先级队列(堆)

目录 一、优先级队列 1、概念 2、优先级队列的模拟实现 2.1 堆的概念 2.2 堆的存储方式 2.3 堆的创建 2.3.1 堆向下调整 2.3.2 堆的创建 2.4 堆的插入与删除 2.4.1 堆的插入 2.4.2 堆的删除 2.5 用堆模拟实现优先级队列 3.常用接口介绍 3.1 PriorityQueue的特性…

WebGIS管线在线编辑器(电力、水力、燃气、热力、热能管线)

随着GIS等信息技术的发展,地下管线管理也从二维平面向三维立体管理迈进。传统管线信息管理系统将管线及其附属设施抽象成二维平面内的点、要素,并使用各类点符号、不同颜色线段进行表达。虽能一定程度上满足城市智慧运行的需要,但不能很直观的…

51单片机学习笔记6 数码管显示

51单片机学习笔记5 数码管显示 一、动态数码管1. 动态数码管工作原理2. 工作过程3. 原理图(1)数码管及74HC245(2)74HC138译码器 4. 74HC245介绍(1)**功能**(2)**引脚**(3…

unity3d——沙盒路径

文章目录 Unity3D中不同平台的沙盒路径: 示例 在Unity3D中,尤其是在移动平台如Android和iOS上,由于系统安全机制,应用程序不能直接访问操作系统的所有文件和目录,而是被限制在一个特定的“沙盒”环境中。这个沙盒是一个…

迷宫(蓝桥杯)——DFS和BFS

迷宫 题目描述 下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可以通行的地方。 010000 000100 001001 110000迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这 个它的上、下、左…

【Linux】Centos7安装redis

目录 下载安装包安装1.解压2.环境安装3.查看redis的安装路径4.将之前redis的配置文件,复制到安装路径下(新建一个文件夹并复制)5.redis 设置默认后台启动,修改配置文件6.启动redis服务默认启动通过配置文件启动查看进程 7.开放637…

52、Qt/窗口、常用类、ui相关学习20240321

一、使用Qt 自由发挥登录窗口的应用场景,实现一个登录窗口界面。 要求: 1. 需要使用Ui界面文件进行界面设计 2. ui界面上的组件相关设置,通过代码实现 3. 需要添加适当的动图。 代码: #include "widget.h" #incl…

【重温设计模式】访问者模式及其Java示例

访问者模式的基本概念 访问者模式,一种行为型设计模式,其基本定义是:允许一个或者多个操作应用到一组对象上,解耦操作和对象的具体类,使得操作的添加可以独立于对象的类结构变化。在面向对象编程中,访问者…

软件推荐 篇三十七:开源免费无广告的在线音乐免费播放 | MusicFree纯净无广告体验-小众冷门推荐

引言 自从QQ音乐没了杰伦、某云开始收费,除了各种广告弹窗导致电脑卡的要死,打工人就靠这点音乐背景熬夜了,木有办法,得有个开源免费的听歌软件吧,一搜github,软件一大堆,作为一个打工仔&#…

八大排序算法之希尔排序

希尔排序是插入排序的进阶版本,他多次调用插入排序,在插入排序上进行了改造,使其处理无序的数据时候更快 核心思想:1.分组 2.直接插入排序:越有序越快 算法思想: 间隔式分组,利用直接插入排序…

java数据结构与算法刷题-----LeetCode215. 数组中的第K个最大元素

java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846 文章目录 解题思路:时间复杂度O( n n n),空间复杂度…

【深度学习】训练Stable Diffusion环境

仓库: https://github.com/bmaltais/kohya_ss.git 基础镜像: from kevinchina/deeplearning:sdxllighting_trt_nginx_002api docker run --net host --gpus device0 -e APIWORKS1 -it t1:t1 bash构建环境: sudo -i git clone https://git…

算法第三十一天-直方图的水量

直方图的水量 题目要求 解题思路 使用面向列的计算比面向行的计算更加容易。我们只需要考虑当前的位置的左右最高模板的高度。 方法一、暴力解法 每个位置能接到多少雨水,很容易想到[木桶效应],即是由两边最短的木板限制的。那么直观思路就是&#x…

C语言 动态内存管理

目录 前言 一、动态内存分配 二、malloc和free函数 2.1 malloc函数 2.2 free函数 三、calloc和realloc函数 3.1 calloc函数 3.2 realloc函数 四、常见的动态内存的错误 1.对NULL指针的解引用操作 2.对动态开辟空间的越界访问 3.对非动态开辟内存使用free释放 4.使用…

深度学习-2.8模型拟合概念和欠拟合模型、过拟合调整策略

模型拟合概念和欠拟合模型、过拟合调整策略 文章目录 模型拟合概念和欠拟合模型、过拟合调整策略一、模型拟合度概念介绍1.测试集的“不可知悖论”2.模型拟合度概念与实验 二、过拟合、欠拟合问题解决方案1. 欠拟合解决方案2.过拟合解决方案 三、神经网络结果选择策略1. 参数和…

拼多多2023年实现营收2476亿 助力品质好物与消费升级双向奔赴

拼多多集团近日发布了截至去年12月31日的财务业绩报告,拼多多在2023年第四季度实现了889亿元的营收,同比增长了惊人的123%。而在全年范围内,拼多多的营收更是高达2476亿元,同比增长了90%。 去年是拼多多全面拥抱高质量发展的元年…

晶体管图示仪 能测 IGBT. Mosfet. Diode. BJT......

STD2000晶体管图示仪系统能测试很多电子元器件的静态直流参数(如击穿电压V(BR)CES/V(BR)DSs、漏电流ICEs/lGEs/IGSs/lDSs、阈值电压/VGE(th)、开启电压/VCE(on)、跨导/Gfe/Gfs、压降/Vf、导通内阻Rds(on))。 测试种类覆盖 7 大类别26分类,包…

解锁企业数字化运营管理:论专业数据中台解决方案的重要性-亿发

没有数据中台,数字化经营就像是建立在沙滩上的城堡,缺乏坚实的基础支撑。数据中台对于企业数字化经营能力的建设至关重要,它扮演着连接、整合和管理数据的关键角色,出现将扩展企业可利用数据的范围。传统的业务分析主要使用财务数…

Python使用PaddleOCR进行图片转文字

PaddleOCR是百度飞桨开发的OCR库 安装 安装PaddleOCR,只需要两个命令: pip install paddlepaddle2.4.2 pip install paddleocr 基本使用 PaddleOCR的使用也很简单: from paddleocr import PaddleOCR# use_angle_cls:是否使用…

高通 8255 基本通信(QUP)Android侧控制方法说明

一:整体说明 高通8255芯片中,SPI IIC UART核心统一由QUP V3 进行控制 QUP V3为可编程模块,可以将不同通道配置为SPI IIC UART通路,此部分配置在QNX侧 QUP 资源可以直接被QNX使用,Android侧可以通过两种方法使用QUP资源…