☰
SpringBoot+JDBC连接MySQL完整配置与避坑指南
2026/9/29 17:19:10 网站建设 项目流程

简介:这是一份围绕Java SpringBoot整合JDBC连接MySQL数据库的完整解决方案,目标读者是Java后端初学者、正在做课程设计或毕业设计的在校生,以及需要在业务项目中快速接入数据库的开发者。压缩包共136个文件,总体积637.42MB,包含.java源码与.class编译产物、.sql数据库脚本、.xml与.properties配置文件,以及dll、exe、msi、zip等运行时依赖和安装工具。其中sql脚本可直接导入数据库,快速生成表结构和演示数据;dll与exe多对应MySQL驱动或系统组件,msi与zip提供不同环境下的安装与免安装选项,便于减少本地环境配置的障碍。资源以Jdbcdemo工程为主体,涵盖用户实体、JDBC控制器、SpringBoot启动类等关键代码,覆盖从数据源配置、SQL执行到结果返回的完整链路,目录结构也按功能模块拆分,便于读者对照学习。目前已有598人学习下载,适合希望利用一套真实工程系统掌握SpringBoot使用JDBC连接MySQL的中初级开发者。

1. SpringBoot + JDBC 连接 MySQL:能落到工程里的那套配置

开发中把 JDBC 和 SpringBoot 接到一起,本身不算什么新东西,可真到动手时,大多数人卡住的位置往往出人意料:不是 SQL 写不对,而是驱动版本和 MySQL 服务端不匹配、SSL 连接报错、连接池默认参数撑不住业务压力。这套解决方案把「SpringBoot 项目通过 JDBC 连接 MySQL」的完整链路整理好了,从 mysql-connector-j 驱动的选型、application.properties 里的数据源配置,到 JdbcTemplate 封装后的增删改查、事务边界控制,再到连接池参数怎么调,均包含在源码包和安装包里。适合两种人:一种是刚接触 SpringBoot 想用最朴素方式把 MySQL 跑通的新手;另一种是接手旧项目,被数据库连接报错和诡异超时反复折腾的开发者。拿到手之后,按下面的步骤配置,基本可以绕开我当年踩过的那些坑。

2. SpringBoot + JDBC 连 MySQL:环境准备与选型思路

在写任何代码之前,先把环境理清楚能省去后面一半的排错时间。这个环节容易翻车的地方主要有三个:MySQL 服务端版本、JDBC 驱动版本、SpringBoot 版本。三者并不是随便组合都能正常工作,版本不匹配时驱动的加载机制会发生改变,常见的报错是java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed或者The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized。

2.1 安装包与版本搭配:先定 MySQL 再定驱动

我一般先装 MySQL 再选驱动。这套方案自带的安装包是社区版,安装时注意两点:一是端口默认 3306,避免和本机已有的 MySQL 实例冲突;二是 root 密码要设置一个自己能记住的,后面连接测试全靠它。装好之后,用命令行验证一下服务是否在监听。

# 检查 MySQL 服务状态(Windows 用 net start | findstr mysql,Linux 用 systemctl) netstat -an | grep 3306 # 结果里出现 0.0.0.0:3306 LISTENING 或 :::3306 LISTEN 说明服务正常

驱动方面,现在统一使用mysql-connector-j(groupId 是com.mysql,artifactId 是mysql-connector-j)。它替代了老式的mysql-connector-java,两者的包名区别很小,但坐标不同。对于 SpringBoot 2.7.x 推荐 8.0.x 系列驱动;对于 SpringBoot 3.x 则需要 8.0.31 以上或新增的 9.x 驱动。这个对应关系非常关键,驱动版本过低在 JDK 17 上直接加载失败,报UnsupportedClassVersionError。

2.2 依赖引入:只需要一个 starter 和一条驱动依赖

SpringBoot 工程里使用 JDBC 其实不需要配置数据源池,只引入spring-boot-starter-jdbc就够了。它会自动把DataSource和JdbcTemplate装配好,但注意默认的连接池是 HikariCP,这是 SpringBoot 官方默认选择,也是性能测试里表现最好的一个。如果你的项目里看到用的是 DBCP 或 C3P0,那就是有人手动排除掉 HikariCP 换的,建议换回来。

<!-- pom.xml 里核心依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency>

这段配置里scope=runtime的意思是编译期不需要驱动类,运行时由 SpringBoot 自动加载。要注意如果你的项目里手动使用Class.forName("com.mysql.cj.jdbc.Driver"),那么在编译期就需要这个依赖,scope 要改成compile或者去掉 runtime 限定。我遇到过一种情况是有人把驱动手动放在 Tomcat 的 lib 目录里,导致和项目内 Maven 依赖重复加载,出现Unable to load authentication plugin 'caching_sha2_password'的报错,这种情况把外部 lib 里的驱动删掉即可。

2.3 初始化数据库:建库建表脚本

源码包里附带了一份init.sql,里边把演示用的用户表、订单表结构和测试数据都准备好了。手动执行一遍的过程最好是在命令行里完成,因为你会直观地看到编码和字符集设置。

mysql -u root -p < init.sql # 提示输入密码后执行,脚本里包含 CREATE DATABASE demo_db DEFAULT CHARACTER SET utf8mb4;

utf8mb4这个字符集很重要。MySQL 8.0 默认字符集是utf8mb4,它才能完整存储四字节的 emoji 和生僻汉字。如果你的表用的是老旧的utf8,遇到特殊字符就会变成???,排查起来非常头疼。建表之后,用SHOW CREATE TABLE user;看一眼实际生效的字符集,确认无误再进下一步。

3. SpringBoot + JDBC:数据源配置与增删改查落地

现在进入正题,把 SpringBoot 项目里的数据源配置好,然后用 JdbcTemplate 写一套完整的增删改查。JdbcTemplate 是 Spring 对 JDBC 的封装,它把连接获取、PreparedStatement 创建、结果集映射这些样板代码都处理掉了,但依然保留 SQL 的直接控制权,不像 MyBatis 或 JPA 那样引入额外的映射层。用 JDBC 的场景,追求的就是对 SQL 的绝对控制。

3.1 application.properties 数据源参数

SpringBoot 的自动配置会读取spring.datasource前缀下的属性来创建数据源。下面这套配置是经过实践验证的,可以直接复制使用。

# 完整的数据源配置,注意 url 中参数的含义 spring.datasource.url=jdbc:mysql://localhost:3306/demo_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true spring.datasource.username=root spring.datasource.password=你的密码 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # HikariCP 连接池参数 spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.idle-timeout=600000

URL 里的参数逐个说:

  • useUnicode=true&characterEncoding=utf8:确保中文以 UTF-8 传输。
  • serverTimezone=Asia/Shanghai:避免 MySQL 8.0 服务器时区和 JVM 时区不一致导致的The server time zone value异常。
  • useSSL=false:本地开发环境无 SSL 证书时关闭加密连接,若为 true 且 MySQL 未开启 SSL 会直接报错。
  • allowPublicKeyRetrieval=true:MySQL 8.0 使用 caching_sha2_password 认证时,如果客户端第一次连接需要从服务器获取公钥,这个参数必须为 true,否则报Public Key Retrieval is not allowed。

连接池参数里maximum-pool-size不是越大越好。MySQL 默认最大连接数是 151,如果多个应用实例共享同一个 MySQL,每个实例都设置过大,会把连接数打满。一般单实例的 web 应用,20 到 50 足够用。

3.2 使用 JdbcTemplate 操作数据库

配置完成之后,JdbcTemplate 会被 SpringBoot 自动注入到任何需要的地方。我写的增删改查示范不搞复杂的封装,直接用最直观的 JdbcTemplate API 演示。

@Repository public class UserRepository { private final JdbcTemplate jdbcTemplate; public UserRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } // 查询单条记录,注意 RowMapper 的使用方式 public User findById(Long id) { String sql = "SELECT id, name, email FROM user WHERE id = ?"; return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> new User( rs.getLong("id"), rs.getString("name"), rs.getString("email") ), id); } // 查询列表 public List<User> findAll() { String sql = "SELECT id, name, email FROM user ORDER BY id DESC"; return jdbcTemplate.query(sql, (rs, rowNum) -> new User( rs.getLong("id"), rs.getString("name"), rs.getString("email") )); } // 更新操作,返回受影响行数 public int updateEmail(Long id, String email) { String sql = "UPDATE user SET email = ? WHERE id = ?"; return jdbcTemplate.update(sql, email, id); } // 删除操作 public int deleteById(Long id) { String sql = "DELETE FROM user WHERE id = ?"; return jdbcTemplate.update(sql, id); } }

queryForObject在查不到数据时会抛出EmptyResultDataAccessException,这是正常行为,不建议用 try-catch 吞掉这个异常,而是应该放行让全局异常处理器去映射成 404。RowMapper里的rs.getLong("id")是根据列名取值的,如果你的 SQL 里用了别名,比如SELECT id AS user_id,那这里就要改成rs.getLong("user_id"),否则会报Column not found错误。

3.3 insert 返回自增主键

插入数据并拿到自增主键是很常见的需求。JdbcTemplate 提供了KeyHolder机制,可以避免先查再插的两次交互。

public Long insertUser(User user) { String sql = "INSERT INTO user(name, email) VALUES (?, ?)"; GeneratedKeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(connection -> { PreparedStatement ps = connection.prepareStatement(sql, new String[]{"id"}); ps.setString(1, user.getName()); ps.setString(2, user.getEmail()); return ps; }, keyHolder); return keyHolder.getKey().longValue(); }

这里prepareStatement(sql, new String[]{"id"})是告诉 MySQL 我们要返回 id 列的自增值。如果写成prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)在多列联合自增场景下可能取到错误的列。keyHolder.getKey()返回的是Number,在 MySQL 下通常是Long,但在某些数据库方言下可能是BigInteger,所以用.longValue()转一下最保险。

4. 连接池调参与排查:事务边界、SSL 错误与驱动不兼容

连接池和事务是 JDBC 方案里最容易出现翻车的地方。连接池参数设置不合理,会表现为接口偶尔变慢、短暂卡顿后恢复;事务边界没理清楚,会出现数据写了但没提交,或者报Transaction silently rolled back。这一章把连接池调优要点和常见问题的排查路径一并整理清楚。

4.1 事务管理:让多步操作保持原子性

SpringBoot 里用@Transactional声明事务非常方便,但很多人不清楚它的默认回滚策略:只回滚RuntimeException和Error,受检异常(比如IOException)不会引起回滚,而且事务会一直保持到方法结束。如果你在事务方法里 catch 到了异常而没抛出,事务同样不会回滚,但数据已经提交了。

@Service public class OrderService { private final JdbcTemplate jdbcTemplate; @Transactional public void createOrder(Order order) { // 第一步插入订单主表 jdbcTemplate.update("INSERT INTO orders(user_id, total) VALUES (?, ?)", order.getUserId(), order.getTotal()); // 第二步扣减库存 jdbcTemplate.update("UPDATE product SET stock = stock - ? WHERE id = ?", order.getProductId(), 1); // 这里如果抛出 RuntimeException,两步都会回滚 } }

事务参数里我建议关注rollbackFor和propagation两个属性。默认的propagation = Propagation.REQUIRED在大多数场景下是对的,即加入已有事务,如果没有则新建。但如果你在事务方法里调用了另一个类的@Transactional方法,并且这个调用是通过this直接调用的,Spring 的代理不会生效,事务会失效。正确做法是通过注入的 Bean 或在同类内使用AopContext.currentProxy()。这个坑几乎每周都会有人在群里问一遍,原因就是没理解代理机制。

4.2 连接池参数怎么调:根据压力而不是拍脑袋

HikariCP 的核心参数需要结合业务场景来定。maximum-pool-size不是越大越好,连接数过多时,即使数据库端没有拒绝连接,MySQL 的线程调度也会成为瓶颈。我通常的做法是先用压力测试工具(如 JMeter)施压,观察响应时间和数据库 CPU 使用率,然后逐渐增加连接数,找到最优值。

参数默认值建议调整方向
minimum-idle10空闲连接数,与最大连接数一致时,池的响应最稳定
maximum-pool-size10压测后按需调整,一般 20 到 50 够用
connection-timeout30000等待连接超时时间,过短可能在高并发下误报超时
idle-timeout600000空闲连接回收时间,小于数据库 wait_timeout 即可
max-lifetime1800000需小于数据库连接最大生命周期,避免使用已断开连接

常见错误是修改idle-timeout大于数据库的wait_timeout值。MySQL 的wait_timeout默认是 8 小时,如果你的idle-timeout设置为 10 小时,连接池认为连接还没到期,但 MySQL 已经主动断开了,这时候取出的连接会报Connection is not available或Communications link failure。稳妥的做法是让max-lifetime至少比数据库wait_timeout短 30 秒。

4.3 避坑:SSL 连接相关报错与驱动版本不兼容

下面是几个比较高频的问题,按「现象 → 原因 → 解决」的方式记录下来。

问题一:明明数据库能连,却报 SSL 连接错误

  • 现象:启动项目报SSLConnectionException: Unable to load default SSLContext或握手失败。
  • 原因:MySQL 8.0 默认开启 SSL 加密连接,而本地的 MySQL 未配置有效证书。URL 里没加useSSL=false,驱动尝试以 SSL 方式连接失败。
  • 解决:在 JDBC URL 中明确添加useSSL=false。如果公司安全要求必须开启 SSL,则需准备证书并在连接串里指定sslMode=VERIFY_CA和证书路径。

问题二:新装的 MySQL 8.0 连接报 Public Key Retrieval is not allowed

  • 现象:连接时报SQLNonTransientConnectionException: Public Key Retrieval is not allowed。
  • 原因:MySQL 8.0 默认密码插件是caching_sha2_password,客户端第一次连接时需要获取服务器的 RSA 公钥,如果客户端驱动不允许则直接失败。
  • 解决:URL 追加allowPublicKeyRetrieval=true。生产环境如果有更高安全要求,可以在 MySQL 端将用户插件改为mysql_native_password,但新驱动对这个插件的兼容性正在逐步调整。

问题三:驱动版本与 SpringBoot 版本不兼容,启动报错或方法找不到

  • 现象:项目启动时报NoClassDefFoundError或AbstractMethodError。
  • 原因:SpringBoot 2.x 使用旧版mysql:mysql-connector-java依赖,与 MySQL 8.0 的服务端协议不完全兼容;SpringBoot 3.x 依赖包名改变为com.mysql:mysql-connector-j,若存在不同版本的驱动被过滤器的类加载机制混用,就会发生此错误。
  • 解决:统一依赖坐标,删除pom.xml中手动指定的旧驱动,或在dependencyManagement中明确指定新版本。检查mvn dependency:tree是否有多个不同版本的驱动共存。

问题四:连接池连接突然全部无效,接口批量报错

  • 现象:系统运行一段时间后,突然大量报Communications link failure或Connection reset,重启后恢复。
  • 原因:连接长时间空闲,被 MySQL 服务端wait_timeout或中间网络设备关闭,连接池里却还保留着这些废旧连接。
  • 解决:设置合理的max-lifetime,比 MySQLwait_timeout短;并开启连接池的connection-test-query(HikariCP 默认自动开启,MySQL 使用SELECT 1)。

5. 验证连接方式与压测配置:最后要形成两个习惯

项目配置完成后,不能只是跑一次main方法看看启动日志就完事,要形成一套可重复的验证流程。我的习惯是:先做「连接有效性验证」,再做「基础压力验证」,两者通过之后才把配置提交到仓库。

连接有效性的最简单办法是写一个ApplicationRunner的测试入口,启动时自动执行一次查询。

@Component public class DatabaseConnectionChecker implements ApplicationRunner { private final JdbcTemplate jdbcTemplate; public DatabaseConnectionChecker(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public void run(ApplicationArguments args) { Integer result = jdbcTemplate.queryForObject("SELECT 1", Integer.class); if (result != null && result == 1) { System.out.println("数据库连接校验通过"); } } }

这段代码里SELECT 1是连接池检测连接健康度的标准方式,开销极低,生产环境里的连接池每分钟都会执行一次。如果SELECT 1都失败,说明连接已失效,HikariCP 会从池中淘汰该连接并重建。

接下来是压力验证。不要一上来就用 JMeter 打满 1000 并发,而是先做小规模测试,比如 50 并发持续 2 分钟,观察三项指标:接口 P99 响应时间、数据库 CPU 使用率、连接池的活跃连接数。若活跃连接数始终低于maximum-pool-size的 70%,说明连接池宽裕,可以考虑调小池子来减少资源浪费;若活跃连接数打满且响应时间上升,说明需要扩大最大连接数或者优化 SQL 索引。

我最后想强调一个排查习惯:每次遇到数据库连接相关的玄学问题,先看SHOW PROCESSLIST和SHOW GLOBAL STATUS LIKE 'Threads_connected'。这两个命令能立刻告诉你当前 MySQL 有多少连接、每个连接在干什么。如果Threads_connected数量异常高,再看SHOW FULL PROCESSLIST里的Command和State字段,要么是连接未释放,要么是某个慢 SQL 长时间占用连接。从那以后,我每次接触一个新的 SpringBoot + MySQL 项目,都会强制先跑一遍这两个命令做一次快照,再开始看代码。这套方案里的源码和安装包也是奔着这个目的准备的——让你把环境问题在进入业务开发前一次性处理干净,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询