用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,一经查实,立即删除!

相关文章

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

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

生产者消费者模式

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

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的步骤。 我们用一个实际的例子来进行…

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

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

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

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

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

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

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

目录 一、C/C内存分布二 、C语言中动态内存管理方式: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 自定义类…

【LAMMPS学习】三、构建LAMMPS(7)具有额外构建选项的软件包

3. 构建 LAMMPS 3.7.具有额外构建选项的软件包 当使用某些包进行构建时,除了Build_package页面上描述的步骤之外,可能还需要额外的步骤。 ​ 对于CMake构建,可能有额外的可选或必需的变量要设置。对于使用make进行构建,可能需…

【C语言】基本语法知识C语言函数操作符详解

主页:醋溜马桶圈-CSDN博客 专栏:C语言_醋溜马桶圈的博客-CSDN博客 gitee:mnxcc (mnxcc) - Gitee.com 目录 1.基本语法 1.1 代码解释 1.1.1 main()主函数 1.1.2 int 1.1.3 { } 1.1.4 printf()库函数 1.1.5 stdio.h头文件 1.2 C语言的…

[C语言]——操作符详解

目录 一.操作符的分类 二.二进制和进制转换 1.二进制转十进制 2.二进制转八进制和十六进制 2.1二进制转八进制 2.2二进制转十六进制 三.原码、反码、补码 四.移位操作符 1.左移操作符 2.右移操作符 五.位操作符:&、|、^、~ 练习1:编写代码实…

3d纸模型图纸制作方法---模大狮模型网

制作3D纸模型图纸通常需要按照以下步骤进行: 选择设计模型: 首先确定你想要制作的3D纸模型的设计,可以是建筑物、动物、交通工具等各种形式的模型。 绘制设计图纸: 使用计算机辅助设计软件(如AutoCAD、SketchUp)或手工绘图工具…

JavaMySQL高级一(下)

目录 1.常用函数 1.字符串函数 2.时间日期函数 3.聚合函数 4.数学函数 2.分布查询 3.子查询基础 1.简单子查询 1.常用函数 在程序开发过程中,除了简单的数据查询,还有基于已数据进行数据的统计分析计算等需求。因此,在SQL中将一…

数据库学习记录(二)多表设计与多表查询

一对多 一对多时候,一张表内的一个数据可能对应着其他表内的多个数据,例如一个部门内有多个员工,但是公司里不只一个部门,也不止一个员工,这个时候就是一对多的情况,这个时候可以绑定一个外键,…

Linux 磁盘的一生

注意:实验环境都是使用VMware模拟 ​ 磁盘接口类型这里vm中是SCSI,扩展sata,ide(有时间可以看看或者磁盘的历史) ​ 总结:磁盘从有到无—类似于建房子到可以住 ————————————————————————————————————…

php laravel 二维码

public function qr($url,$name2,$inpath){require_once(dirname(__FILE__) . /../../../Library/phpqrcode/phpqrcode.php);$errorCorrectionLevel L;//容错级别$matrixPointSize 10;//生成图片大小$QRcode new \QRcode() ;$QRcode->png($url, $inpath.$name2, $errorCor…

YOLOv5改进系列:Efficientrep结构助力涨点

一、论文理论 本文提出一种硬件友好的卷积神经网络结构,该结构类似于repvgg。在衡量网络效率时,经常使用Flops或者参数量,这些衡量指标对于硬件计算能力和内存带宽不敏感。因此,如何设计一个神经网络架构,使其有效地利用硬件计算能力和内存带宽是至关重要的。 论文地址:…