ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot + JDBC + MySQL 实战:环境搭建、连接池调优与避坑指南

SpringBoot + JDBC + MySQL 实战:环境搭建、连接池调优与避坑指南 简介这份Java Spring Boot通过JDBC连接MySQL数据库的完整解决方案定位清晰适合具备一定Java基础、正为驱动配置或环境搭建困扰的初中级开发者也适合小型项目需要快速打通数据链路的场景同时可服务于课堂练习与个人作品开发。资源包共136个文件压缩后约637MB内部按代码、配置、脚本和运行依赖分模块组织包含Java源码与编译后的class文件、SQL建表初始化脚本、XML与properties配置同时提供MySQL相关的dll、exe、msi等安装组件及pem证书、PDF说明等辅助材料整体结构清晰便于按需取用。目前已有598人学习下载。借助该资源读者可拿到可运行的Spring Boot示例工程含JdbcController等接口类、完整数据库脚本、驱动与连接配置参考以及必要的Windows环境安装包。这些内容既能直接用于本地开发调试也能作为后续项目复用的脚手架能大幅缩短环境准备到数据库连通验证的时间并降低新手的学习门槛。1. SpringBoot JDBC这套方案先弄明白它是干嘛的SpringBoot 默认全家桶好用反而把 JDBC 的基本功掩盖了。接手老项目遇到查询慢、连接池报错、时区把时间存成前一天打开日志看到 SQL 一堆问号却没法快速验证问题多半就藏在框架包起来的那层黑匣子里。这套用 JDBC 连 MySQL 的方案就是把外壳拆掉自己管 Connection、自己写 SQL、自己控参数慢在哪一眼可见。它适合想从 JPA / MyBatis 的映射和代理里跳出来、做轻量数据访问层的开发者也适合刚学 SpringBoot 想先把数据库链路吃透的新手。全文按实际交付的顺序来装 MySQL、对驱动、建工程、调连接池、踩坑一条路通到能上线。2. 环境准备MySQL 5.7 / 8.0 怎么选、怎么装、驱动版本怎么对动手写代码之前先把数据库和驱动这对地基打稳。很多人一上来就改 pom.xml结果启动时报ClassNotFoundException或者CommunicationsException回头才发现是 MySQL 版本、驱动坐标、连接参数三者没对上。这一章把环境选择讲清楚后面代码才不会翻车。2.1 MySQL 5.7 与 8.0 的取舍别被“版本太高”卡住选 MySQL 版本先看你的服务器、运维习惯和团队熟悉程度不是越新越好也不是 5.7 越老越稳。MySQL 5.7 和 8.0 在 JDBC 连接层面的核心差别有两个认证插件和默认字符集。MySQL 5.7 默认认证插件是mysql_native_password老驱动比如 5.x 的 mysql-connector-java可以直接连很多老项目和云厂商 RDS 还在用它。MySQL 8.0 默认认证插件改成了caching_sha2_password如果你的 JDBC 驱动版本太旧第一次连接就会报Authentication plugin caching_sha2_password cannot be loaded。这不是 SQL 写错了纯粹是驱动和认证方式对不上。MySQL 8.0 还顺手把 utf8 的默认排序规则改成了utf8mb4_unicode_ci字符集方面更省心。所以我的建议很直接全新项目直接用 MySQL 8.0驱动用 8.x 版本URL 里把allowPublicKeyRetrievaltrue加上如果项目已经跑在 5.7 上没有迁移计划就用 5.7.44 这个版本打底。5.7.44 是 5.7 系列的后期维护版本网上关于 mysql 5.7.44 安装过程详细的资料最多踩坑经验也全新环境照着做基本能一遍过。对比项MySQL 5.7MySQL 8.0默认认证插件mysql_native_passwordcaching_sha2_password对老驱动友好度高低驱动 5.1.49 以下连不上utf8mb4 支持可用需手动指定默认更完善窗口函数不支持支持JDBC URL 额外参数一般不需要建议加 allowPublicKeyRetrievaltrue如果你要在一台 Windows10 机器上装 MySQL 练手优先考虑 MySQL 8.0 的 ZIP 免安装版干净好卸载。下面这节就按这个思路写。2.2 Windows / Linux 装 MySQL 的最小步骤初始化、起服务、建账号Windows 上最常见的安装方式是下载 ZIP 解压包或者用图形化安装向导。图形化下一步下一步没什么好讲的容易出问题的是解压版。解压完以后先确认bin目录已经加进了系统 PATH然后在my.ini里把basedir和datadir指对执行下面三条命令完成初始化和注册服务mysqld --initialize-insecure mysqld --install MySQL8 net start MySQL8--initialize-insecure会生成一个没有密码的 root 账号本地登录后立刻改密码。--install MySQL8是把 MySQL 注册成 Windows 服务服务名你自己定后面net start启动的就是这个名字。这一步如果提示Install/Remove of the Service Denied就是当前终端没有管理员权限换成“以管理员身份运行”再执行。Linux 上用系统包管理器装更省事Debian/Ubuntu 执行sudo apt update sudo apt install mysql-serverCentOS/RHEL 对应sudo yum install mysql-server装完用sudo systemctl enable --now mysql开机自启并立刻启动。包管理器装的一般自带初始化脚本不需要手动mysqld --initialize。启动之后用 root 登录建库建账号。JDBC 连接里不建议一直用 root最小权限原则在开发阶段就该养成CREATE DATABASE demo_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER demo% IDENTIFIED BY demo_pass; GRANT SELECT, INSERT, UPDATE, DELETE ON demo_db.* TO demo%; FLUSH PRIVILEGES;demo%里的%表示允许任意主机远程连接。生产环境建议缩成具体 IP 段比如demo192.168.1.%。这里只给了增删改查权限没有给DROP、CREATE目的是防止应用账号误操作表结构这个习惯后面能救你一次。2.3 JDBC 驱动驱动类、坐标改名与版本匹配的硬规则驱动版本是 SpringBoot JDBC 方案里最阴间的部分。老项目里经常看到com.mysql.jdbc.Driver这是 MySQL 5.x 时代 mysql-connector-java 5.1.x 的驱动类名MySQL 8.0 之后驱动类名变成了com.mysql.cj.jdbc.Driver字母多了一段cj.别写错。pom.xml 里的依赖坐标也要注意MySQL 官方后来把项目名从mysql-connector-java改成了mysql-connector-j所以新版本 SpringBoot 工程里拿到的坐标是下面这种dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency只要 SpringBoot 父工程管着版本号你不需要手动写version它会按当前 SpringBoot 版本选择一个配套的驱动版本。真正的问题往往出在“手动加了老版本号”或者“拿 5.1.49 驱动去连 MySQL 8.0”前者把 SpringBoot 自动配置的版本覆盖掉了后者直接验证失败。我一般会遵守两条硬规则驱动版本不低于 8.0.13MySQL 服务端 5.7 或 8.0 都统一用这个驱动SpringBoot 版本再高也不用慌坐标用com.mysql:mysql-connector-j就对了spring-boot-starter-jdbc自带的版本管理会自动选一个兼容版本。如果实在不确定启动后看日志里打印的HikariPool - Start completed后面的版本号或者跑一条 SQL 查版本SELECT VERSION();驱动、服务端、SpringBoot 三者版本对上以后环境这关才算过去。3. 搭建最小可运行的 SpringBoot JDBC 工程pom、yml、Repository 一个不少环境好了就该搭工程了。很多人喜欢用 Spring Initializr 在网页上生成项目生成完直接 import 进来这没问题。关键是别把不必要的依赖勾进来比如spring-boot-starter-data-jpa一旦勾了JDBC 的自动配置会被 JPA 的 EntityManager 遮住你后面手写 JDBC 代码虽然能跑但事情解释起来就绕了。这套方案只依赖两个关键 starter。3.1 工程骨架pom.xml 里到底要放哪几个依赖一个标准的 Maven 工程目录结构就是src/main/java、src/main/resources这两套再加上一个 pom.xml。对于 JDBC 方案pom.xml 里的依赖非常轻parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency /dependencies第一个依赖spring-boot-starter-jdbc是核心它会自动引入spring-jdbc和 HikariCP 连接池还会触发 DataSource 的自动配置。第二个依赖是 MySQL 驱动runtime作用域表示编译代码时用不到它启动时由 JDBC 驱动管理器按 URL 加载。有些教程还会让你加spring-boot-starter-web如果你只需要跑一个命令行验证程序web starter 加不加都行如果后面打算写接口测试加上它顺便获得 Tomcat 嵌入式容器。切记不用加 mybatis 或 jpa 的 starter这套方案的手写 SQL 风格跟 ORM 冲突加了反而给新手造成“为什么我的 Repository 没人管”的错觉。3.2 配置数据源application.yml 的连接参数与连接池在src/main/resources/application.yml里写数据源配置。我提供一个开发环境足够用、生产环境也基本安全的基线spring: datasource: url: jdbc:mysql://localhost:3306/demo_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8allowPublicKeyRetrievaltrue username: demo password: demo_pass driver-class-name: com.mysql.cj.jdbc.Driver hikari: minimum-idle: 2 maximum-pool-size: 8 connection-timeout: 3000 idle-timeout: 60000 max-lifetime: 1800000逐个参数说清楚。URL 里的serverTimezoneAsia/Shanghai解决时区问题没有这个参数MySQL 8.0 会报The server time zone value或者把本地时间当成 UTC 存进库后文避坑章会专门讲。characterEncodingutf8保证写入中文不乱码实际 MySQL 端字符集建议对齐到 utf8mb4所以数据库建库时用了utf8mb4这里的连接字符集用 utf8 兼容性最好但如果你非要写utf8mb4也可以驱动会解析。useSSLfalse是本地开发省去证书配置生产环境如果走内网可以保持 false公网必须按运维要求开 SSL。password: demo_pass这里我给密码加了双引号因为 YAML 解析时如果密码是纯数字比如123456不加引号会被当成数字类型某些场景下 SpringBoot 读取时会出错。养成习惯密码一律用引号包住。spring.datasource.hikari下的四个参数是连接池的后面第四章会展开调优这里先记住connection-timeout: 3000表示拿连接最多等 3 秒超过就抛异常这个值治“应用卡死但日志里没有报错”特别有用。写完 yml启动类还是常规的SpringBootApplication不需要额外写任何Configuration。SpringBoot 自动配置会扫描到spring.datasource.url和username然后给你创建一个 HikariDataSource 的 Bean注入到任何需要DataSource的地方。3.3 手写一个 RepositoryConnection 怎么拿、SQL 怎么执行这是整套方案的核心。我们不引入 JdbcTemplate直接拿最原始的 JDBC API写一个查询用户订单的 RepositoryRepository public class OrderRepository { private final DataSource dataSource; public OrderRepository(DataSource dataSource) { this.dataSource dataSource; } public ListOrder findByUserId(Long userId) { String sql SELECT id, user_id, amount, created_at FROM orders WHERE user_id ? ORDER BY created_at DESC; ListOrder result new ArrayList(); try (Connection conn dataSource.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setLong(1, userId); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Order order new Order(); order.setId(rs.getLong(id)); order.setUserId(rs.getLong(user_id)); order.setAmount(rs.getBigDecimal(amount)); order.setCreatedAt(rs.getTimestamp(created_at).toLocalDateTime()); result.add(order); } } } catch (SQLException e) { throw new IllegalStateException(查询订单失败, userId userId, e); } return result; } }这段代码里有两个地方新手最容易看走眼。第一dataSource.getConnection()拿到的连接不是每一次都新建HikariCP 会把连接放在池子里复用所以用完必须归还try-with-resources在这里就是为了确保 Connection、Statement、ResultSet 三个资源自动关闭。关闭顺序是从内到外ResultSet 先关再关 PreparedStatement最后还回 Connection。如果你自己写finally乱关可能出现“ResultSet 已经关了还在读”的异常。第二SQL 里的查询参数用?占位然后ps.setLong(1, userId)绑定真实值。这条规范请无论如何不要违背它防的是 SQL 注入。有人喜欢写成字符串拼接WHERE user_id userId一旦 userId 来自用户输入等于把数据库裸奔给攻击者。用PreparedStatement不只是防注入MySQL 服务端还会缓存执行计划同一条 SQL 重复执行时性能更好。这个 Repository 构造器用了构造器注入SpringBoot 会自动把 HikariDataSource 传进来。没有Autowired注解也不要紧Spring 4.3 之后单构造器就支持隐式注入。如果你想验证这一段 JDBC 代码是否真的连上了库直接在任意SpringBootTest测试类里注入 OrderRepository调用findByUserId能返回数据就算跑通。如果你觉得每个方法都写一遍getConnection、prepareStatement、executeQuery太啰嗦那说明你离下一步不远了——用JdbcTemplate精简。但先别急把原生 JDBC 写一遍你才能真正理解后面所有封装层在干什么。4. 连接池与参数调优HikariCP 的必调参数与事务边界原始 JDBC 代码跑通之后连接池参数就是下一个决定应用生死的点。SpringBoot 从 2.0 开始把 HikariCP 设为默认连接池也就是说你只要引入了spring-boot-starter-jdbc连接池已经存在只是参数是默认值。默认值适合开发环境但不一定适合你的生产峰值。4.1 为什么连接池选 HikariCP默认参数到底够不够Java 生态里的连接池常见的就三个HikariCP、Druid、dbcp2。SpringBoot 官方默认集成 HikariCP原因很简单它的 bytecode 级别优化让获取连接的延迟极低同样是 30 个并发线程抢连接Hikari 的表现通常优于 Druid线程等待时间更短。Druid 的强项是监控功能全有 SQL 慢查询统计、防 SQL 注入拦截很多国内团队图省事直接用 Druid但它在 SpringBoot 2.7 里要手动排除 Hikari 再引入 Druid 的 SpringBoot starter配置成本更高。如果只需要一个足够快、跟 SpringBoot 无缝集成的连接池Hikari 就是最优解。默认参数确实够跑通但有几个默认值放到生产环境必坑minimumIdle默认等于maximumPoolSize也就是连接池不会自动缩容connectionTimeout默认 30 秒一但连接池被占满新请求会傻等半分钟才开始报错maxLifetime默认 30 分钟如果 MySQL 服务端的wait_timeout小于这个值连接会被 MySQL 主动掐断池子里还存着已失效的连接。4.2 必调参数maximumPoolSize、minimumIdle、connectionTimeout、maxLifetime先把四个参数的语义和边界列出来配置代码跟在后面。参数默认值作用生产建议maximumPoolSize10池中最大连接数按数据库 CPU 核数 * 2 磁盘数估算起步 816minimumIdle maximumPoolSize池中最小空闲连接数想要缩容就设置 25不缩容保持默认connectionTimeout30000 ms获取连接最大等待时间3000 ms避免请求积压idleTimeout600000 ms空闲超过该时间且minimumIdle小于maximumPoolSize时释放连接60000 ms 左右maxLifetime1800000 ms连接最大存活时间应小于 MySQLwait_timeout1800000 ms 合理别超过数据库侧超时对应的 Hikari 配置在 yml 里已经写过了这里重点解释maximum-pool-size怎么算。有一个流传很广的公式是CPU 核数 * 2 磁盘数但公式前提是所有查询都是 CPU 密集型的纯计算任务。实际业务系统里一个请求可能产生多条数据库往返还有外部接口等待连接数要按“磁盘、网络、数据库线程并发压力”综合估算。我见过一个 4 核 8G 的实例连接池开 30 都没打满另一个 16 核机器连接池只开了 10 就把数据库 CPU 打到了 80%。所以最靠谱的做法是先设一个保守值压测看连接池等待时间和数据库 CPU再逐步调大。调参还有一个容易被忽略的边界minimumIdle如果设置得比maximumPoolSize小Hikari 才会按需创建新连接两者相等时池子始终保持固定连接数启动后立即创建所有连接。对追求低延迟而不在乎多余连接的应用相等没什么问题对长跑的后台服务我一般会把minimumIdle设为 2让连接数在低谷期自动回落。spring: datasource: hikari: minimum-idle: 2 maximum-pool-size: 8 connection-timeout: 3000 idle-timeout: 60000 max-lifetime: 18000004.3 事务与批量写入手动提交还是 Spring 的 Transactional连接池参数调好之后最常踩的坑就是事务。原生 JDBC 里Connection默认是自动提交模式每执行一条 SQL 就立刻 commit。想保证多条 SQL 要么全成功要么全失败必须先setAutoCommit(false)然后手动 commit 或 rollback。我给出批量插订单的完整代码注意它没有用 try-with-resources 包 Connection因为 catch 里必须能拿到同一个 Connection 做回滚public void batchInsertOrders(ListOrder orders) { String sql INSERT INTO orders(user_id, amount, created_at) VALUES (?, ?, ?); Connection conn null; try { conn dataSource.getConnection(); conn.setAutoCommit(false); try (PreparedStatement ps conn.prepareStatement(sql)) { for (Order order : orders) { ps.setLong(1, order.getUserId()); ps.setBigDecimal(2, order.getAmount()); ps.setTimestamp(3, Timestamp.valueOf(order.getCreatedAt())); ps.addBatch(); } ps.executeBatch(); } conn.commit(); } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { e.addSuppressed(ex); } } throw new IllegalStateException(批量写入失败, e); } finally { if (conn ! null) { try { conn.close(); } catch (SQLException ignored) { } } } }这段代码里三个地方要重点记住。第一PreparedStatement用 try-with-resources 包住确保 Statement 先关闭而 Connection 放在外层手动控制是为了异常时还能 rollback。第二ps.addBatch()不会真正发给 MySQL只有ps.executeBatch()才一次性把所有 SQL 发过去减少了网络往返批量 1000 条实测性能比逐条 insert 快一个数量级。第三conn.commit()提交后finally里把连接还给连接池连接池会重置连接状态但下一次使用前最好还是显式确认setAutoCommit(true)防止连接池复用的连接还停在手动提交模式。如果你不想自己控制这些Spring 的Transactional注解可以接管事务边界。它就是通过 AOP 在方法前后调用commit和rollback效果和你手写这段代码一样但要求事务方法必须由 Spring 代理调用同类内部this调用会失效这个坑放在下一章避坑列表里详细说。5. 排查与避坑JDBC 连 MySQL 最容易翻车的 4 个地方JDBC 连接 MySQL 的报错翻来覆去就那几类但每种都能让人折腾一下午。我把实际遇到的四个典型问题按“现象 → 原因 → 解决”写出来做项目遇到同款直接照做。5.1 启动就报 CommunicationsException端口没起来还是被防火墙拦了现象是应用启动或第一次请求时报com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure后面往往跟着The driver has not received any packets from the server。这种报错八成不是驱动问题而是 MySQL 根本没在监听你连的那个地址。先确认服务是否在跑Windows 上net start看服务状态Linux 上systemctl status mysql。服务活着就查端口监听netstat -an | grep 3306本地监听看到127.0.0.1:3306但应用配置里写的是192.168.x.x:3306那就是 MySQL 的bind-address只绑定了回环地址需要改 my.ini / my.cnf 里的bind-address0.0.0.0并重启。如果监听正常但连接还是被拒再查防火墙是否放行 3306 端口。Windows 开发机上常常是 Windows Defender 防火墙弹出拦截框被误选了取消Linux 上检查firewall-cmd --list-ports或 ufw 状态。最隐蔽的情况是云服务器安全组只放行了 22 和 803306 根本没开本地 telnet 不通。这类问题在云环境里快速定位的方式是写一个最简单的 Java main 方法用DriverManager.getConnection去连数据库这样能排除 SpringBoot 自动配置的干扰报错信息更干净。5.2 时间错乱差8小时时区参数为什么必须写在 URL 里现象是往 MySQL 里插入2025-01-01 12:00:00查出来变成2025-01-01 04:00:00或者反过来时间永远差 8 个小时。原因是 JVM 默认时区和你 MySQL session 的time_zone不一致。MySQL 8.0 的time_zone默认是SYSTEM它读的是操作系统时区如果服务器时区是 UTC而 Java 用的是Asia/Shanghai两边换算就差了 8 小时。解决办法最省事的就是在 JDBC URL 里显式指定时区驱动会按这个参数把java.util.Date转成数据库时间不需要改服务器配置jdbc:mysql://localhost:3306/demo_db?serverTimezoneAsia/Shanghai这里要提醒一点不要写serverTimezoneGMT%2B8GMT8 是固定偏移不处理夏令时逻辑虽然中国没有夏令时但代码可读性和跨时区复用都不如Asia/Shanghai。如果你项目中用LocalDateTime配合 Java 8 时间 API 就不会有时区换算问题如果还停留在java.util.Date建议尽快迁移。5.3 ClassNotFoundException驱动类找不到SpringBoot 版本太高也来凑热闹现象是启动时报ClassNotFoundException: com.mysql.jdbc.Driver或者Unable to load authentication plugin caching_sha2_password。原因是驱动类名写错了或者驱动 jar 版本太低。如果你的 SpringBoot 版本比较新比如 3.xpom 里依赖坐标已经是com.mysql:mysql-connector-j就不能再用 5.1.x 时代的com.mysql.jdbc.Driver类名要改成com.mysql.cj.jdbc.Driver。我见过一个典型案例开发环境 SpringBoot 2.7 跑得好好的某天同事升级到 SpringBoot 3.2发现数据库连不上报的还是驱动类找不到。实际原因不是驱动被删了而是 SpringBoot 3 的自动化配置里对com.mysql.cj.jdbc.Driver的探测路径发生了变化此时只要在 yml 里显式写driver-class-name: com.mysql.cj.jdbc.Driver就能绕过。还有一种情况是你手动在 pom 里写了老版本驱动version5.1.49/version把 SpringBoot 自动管理的版本覆盖了5.1.49 驱动连 MySQL 8.0 必然失败。排除思路就是逐步减少人为变量先去掉版本号用默认不行再手动升级驱动版本。5.4 Transactional 不生效回滚没触发订单数据照样进库现象是方法加了Transactional(rollbackFor Exception.class)执行到一半抛了RuntimeException但数据库里数据还是进去了事务跟没写一样。第一个原因是同类里的this调用。Spring 的声明式事务靠 AOP 代理实现外部调用 Bean 时拿到的是代理对象但this.batchInsert()是直接调用原始对象代理根本不经过事务自然不生效。解决方式是把事务方法挪到另一个 Bean 里或者注入自己的代理Autowired private OrderRepository self;然后用self.batchInsert()。第二个原因是异常被 catch 吞掉。事务代理只会在方法向外抛出异常时触发 rollback如果你在 try-catch 里把异常处理了事务不会知道出了事。解决办法是 catch 里做补偿或者把方法改为不捕获、让异常上抛。第三个原因是 MySQL 表用了 MyISAM 引擎MyISAM 本身不支持事务即使代码里 rollback 了也只是表面丢给服务端数据早已物理写入。排查方式是执行SHOW TABLE STATUS WHERE Name orders看Engine字段是不是InnoDB。建表时默认就是 InnoDB但网上那些老教程里的建表语句可能带着ENGINEMyISAM复制过来就埋雷。6. 进阶用 JdbcTemplate 把 JDBC 代码减半再验证一遍参数化 SQL原生 JDBC 写多了你会发现 70% 的代码在做同一件事拿连接、建 Statement、遍历 ResultSet、关资源。这些样板代码不出错但毫无业务价值。Spring 的JdbcTemplate就是官方封装好的那一层它底层仍然走的是 JDBC API但把样板代码藏了起来SQL 保持原样可见。Repository public class OrderRepository { private final JdbcTemplate jdbcTemplate; public OrderRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate jdbcTemplate; } public ListOrder findByUserId(Long userId) { String sql SELECT id, user_id, amount, created_at FROM orders WHERE user_id ? ORDER BY created_at DESC; return jdbcTemplate.query(sql, (rs, rowNum) - { Order order new Order(); order.setId(rs.getLong(id)); order.setUserId(rs.getLong(user_id)); order.setAmount(rs.getBigDecimal(amount)); order.setCreatedAt(rs.getTimestamp(created_at).toLocalDateTime()); return order; }, userId); } }看到没原来的try-with-resources和catch SQLException全没了换成一个 Lambda 表达式做行映射。JdbcTemplate内部会处理连接的获取和释放也会把SQLException翻译成 Spring 的DataAccessException业务代码里不用再去纠结关闭顺序。进阶不只是写起来舒服还提供了NamedParameterJdbcTemplateSQL 里可以用:userId这种具名参数替代问号复杂查询可读性好很多。验证 JDBC 连 MySQL 是否正常也可以写一个极简的测试方法执行一条无业务噪音的查询Test void should_connect_to_mysql() { Integer version jdbcTemplate.queryForObject( SELECT 1, Integer.class); System.out.println(连接成功数据库返回 version); }我现在的习惯是工程里始终保持一套“原生 JDBC JdbcTemplate”双版本的 Repository 放在 test 目录里当基准任何环境变更都先跑一遍这两个方法。环境本身的问题不该让业务代码背锅。希望这篇能帮你少踩几个坑。本文还有配套的精品资源点击获取
返回列表