用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;@Asyncpublic 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);}

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kafka的分区(partition和副本)

在 Kafka 中,分区(Partition)是一个逻辑上的概念,它将一个主题(Topic)中的消息进行分组。每个分区都有一个唯一的编号,称为分区 ID(Partition ID),它从 0 开始…

许战海战略文库|橋頭品牌突破区域局限打透全国市场的4个建议

自宣统元年(公元1908年)起,重庆南岸区海棠溪古石桥边诞生了一家传奇火锅店——桥头火锅。凭借深厚的文化底蕴和独特的风味,历经百年沉淀桥头火锅已成为南岸区乃至中华餐饮文化中的璀璨明珠。 桥头火锅不仅荣获了“中华老字号”和“中国名菜”两项殊荣&a…

生产者消费者模式

生产者消费者模式 如何用 BlockingQueue 实现生产者消费者模式如何用 Condition「条件变量」 实现生产者消费者模式如何用 wait/notify 实现生产者消费者模式扩展 生产者消费者模式,生产者消费者模式是多线程编程中非常常见的一种设计模式,它被用于解决生…

部署Elasticsearch集群,实现海量航迹数据存储

文章目录 引言I 集群的部署方式1.1 单主节点1.2 多主节点1.3 节点的角色1.4 查询集群状态II 单主节点2.1 安装Master Node2.2 安装Slave nodeIII 主主模式3.1 node-13.2 node-23.3 参数含义IV Logstash4.1 通过下载来安

【DL经典回顾】激活函数大汇总(三十七)(PLU附代码和详细公式)

激活函数大汇总(三十七)(PLU附代码和详细公式) 更多激活函数见激活函数大汇总列表 一、引言 欢迎来到我们深入探索神经网络核心组成部分——激活函数的系列博客。在人工智能的世界里,激活函数扮演着不可或缺的角色,它们决定着神经元的输出,并且影响着网络的学习能力与…

Python从入门到精通秘籍十

一、Python之了解异常 当在Python中执行代码时,如果发生错误,就会抛出异常(Exception)。处理异常是编写健壮的代码的重要部分。Python提供了try-except语句来捕获和处理异常。 下面是使用Python代码详细讲解异常处理的例子&…

PyGWalker:Python中的Tableau,数据可视化变得如此简单!

文章目录 1介绍2 安装3 使用4 将数据可视化导出为代码5 总结 1介绍 在数据分析和可视化的领域,Tableau凭借其强大的功能和直观的界面,一直以来都是专业人士的首选工具。然而,对于许多用户而言,Tableau的封闭性和高昂的成本使其难…

从0到1:Java构建高并发、高可用分布式系统的实战经验分享

文章目录 引言基础架构选择与设计微服务架构分布式储存与计算 高并发处理策略异步处理与消息队列并发控制与资源隔离 高可用性设计与故障恢复冗余与集群化容错与自我修复监控与运维自动化 引言 随着互联网业务的快速发展和技术迭代升级,作为Java架构师,…

【MySQL】对数据库的操作以及数据库备份相关操作

👦个人主页:Weraphael ✍🏻作者简介:目前学习计网、mysql和算法 ✈️专栏:MySQL学习 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论&#x1f4ac…

有ai写文案的工具吗?分享5款好用的工具!

在数字化时代,人工智能(AI)已渗透到我们生活的方方面面,包括内容创作领域。AI写文案的软件以其高效、便捷的特点,正逐渐受到广大内容创作者、营销人员、甚至普通用户的青睐。本文将为您盘点几款热门的AI写文案软件&…

NetSuite 固定资产Write-Off的撤回操作

之前我们有说到如果是Sale了固定资产后发现有误,需要撤回操作该如何处理。这篇文章来补充一下,如果是误Write-Off了一个固资该如何处理,其逻辑与Sale的撤回基本一致,但是少了删除Sale Invoice的步骤。 我们用一个实际的例子来进行…

突破编程_C++_C++11新特性(右值引用与移动语义)

1 右值引用 1.1 右值引用的基本概念 右值引用是 C11 中引入的一个关键特性,它允许程序员显式地将一个表达式标记为右值,从而可以利用移动语义进行优化。在深入探讨右值引用的基本概念之前,首先需要理解左值和右值的概念。 在 C 中&#xf…

HTML5:七天学会基础动画网页(end)

想了想还是有一点东西还没说,当然这块内容也比较简单,就是当我们有一段完整素材时,如下: 我在网上随便找的素材,当然我们平时在使用素材时要注意尊重他人的著作权,不管是字体图片还是别的,不然后面不小心侵…

字符串筛选排序 - 华为OD统一考试(C卷)

OD统一考试(C卷) 分值: 100分 题解: Java / Python / C++ 题目描述 输入一个由n个大小写字母组成的字符串, 按照 ASCII 码值从小到大的排序规则,查找字符串中第 k 个最小ASCII 码值的字母(k>=1) , 输出该字母所在字符串的位置索引(字符串的第一个字符位置索引为0) 。…

NS3 使用 waf 工具添加外部库

我最近在写 NS3 的时候想要把他人写好的外部库添加到 NS3 中一起编译&#xff0c;在 Linux 系统中&#xff0c;添加外部库往往通过编译选项 -l<外部库名> 来添加&#xff0c;而在大型项目中往往需要把外部库写到 Makefile 文件中通过 make 来编译。奈何 NS3 的早期版本都…

org.springframework.beans.factory.BeanNotOfRequiredTypeException异常处理

目录 一、问题详情 二、示例代码 三、原因分析 四、解决方案 一、问题详情 在本地启动项目的时候,突然报了如下错误,导致整个项目启动失败了。 org.springframework.bea

【蓝桥杯备赛】Day15:递推与递归(倒计时23天)

题目1:题目 2335: 信息学奥赛一本通T1422-活动安排 设有n个活动的集合E{1,2,…,n}&#xff0c;其中每个活动都要求使用同一资源&#xff0c;如演讲会场等&#xff0c;而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi…

【微信小程序】零基础快速入门

微信小程序 小程序与普通网页开发的区别 1 运行环境不同 网页运行在浏览器环境中 小程序运行在微信环境中2 API 不同 由于运行环境的不同,所以在小程序中,无法调用 DOM 和 BOM 的API 但是,小程序中可以调用微信环境提供的各种 API,例如:地理位置、扫码、支付等等3 开发模…

【C++】CC++内存管理

目录 一、C/C内存分布二 、C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free三、 C内存管理方式3.1 new/delete操作内置类型3.2 new和delete操作自定义类型3.3 长度域 四、operator new与operator delete函数五、new和delete的实现原理5.1 内置类型5.2 自定义类…

Lombok:@Singular集合元素灵活添加利器

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 一、Singular介绍 二、使用示例 三、注意事项 四、使用场景 总结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、Singular介绍 Sing…