尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Spring Boot连接MySQL完整指南:从配置到增删改查实战

发布时间:2026/9/29 15:32:14

资讯中心
01
ARTICLE

Spring Boot连接MySQL完整指南:从配置到增删改查实战

Spring Boot连接MySQL完整指南:从配置到增删改查实战
Spring Boot 可以说是目前做个人项目、课程设计和中小型业务系统时最常用的 Java 框架而 MySQL 又几乎成了本地开发的默认数据库。两件事单拎出来都不难但放到一起版本、驱动、连接池、字符集、SSL 认证这些环节就像接力赛一样一环扣一环稍不留神就会出现“本地连不上数据库”这种让人抓狂的局面。这篇内容我基于前后端分离开发时的真实踩坑经历把 Spring Boot 项目本地连接并操作 MySQL 数据库的完整链路重新捋了一遍从版本选择、MySQL 安装、数据源配置到实体映射、增删改查接口实现再到最后的异常排查全部按操作顺序展开。不管你是刚学 Java Web、正在做毕设还是准备面试时想补一下 Spring Boot 与 MySQL 的细节都可以照着这份步骤走。1. 先想清楚Spring Boot 连 MySQL 的链路到底是怎么走的1.1 一次完整请求从 Controller 到表的调用顺序很多人写 Spring Boot 项目时会有一种错觉只要在配置文件里填上数据库地址和密码代码里就能自动拿到数据。其实背后有个标准的调用链只是框架帮我们藏住了细节。当你向本地服务发起一个请求比如GET /students请求先打到 Controller 层Controller 再调用 ServiceService 调用 Repository 或者 Mapper 接口。这里有一个很关键的概念Spring Boot 的自动装配机制会在项目启动时读取spring.datasource.*配置帮你创建数据源和连接池。连接池里维护的是一批到 MySQL 的物理连接你的增删改查操作并不是每次新建连接而是从池子里借一个连接执行完再还回去。真正执行 SQL 的是 JDBC Driver也就是我们常说的mysql-connector-j驱动。驱动负责把数据库 URL、用户名、密码转成 MySQL 服务端能理解的握手协议再通过 TCP 3306 端口把 SQL 命令发过去MySQL 执行完再把结果集返回给驱动。Spring Boot 里的 JPA 或 MyBatis 只是在驱动上层再做一次对象关系映射把结果集变成 Java 对象而已。搞清楚这条链路有什么用最大的作用就是定位问题。如果报错出现在 URL 解析那是配置问题如果报错是驱动加载失败那是依赖问题如果报错卡在 SSL 握手那是服务端认证方式问题。很多新手看到一大段异常就直接搜“Spring Boot 连不上 MySQL”但真正有效的处理方式是先判断异常发生在链路中的哪一段。1.2 本地开发环境与生产环境差在哪里本地开发环境通常比较随意MySQL 和 Spring Boot 都在同一台电脑上网络没有隔离账号权限也放得很宽。而生产环境往往是独立数据库服务器有独立的账号、端口、SSL 要求甚至还会通过安全组限制访问来源。这种差异会直接影响配置写法。本地图省事通常用 root 账号或者一个拥有全库权限的账号useSSLfalse也问题不大。但生产环境如果照搬本地配置轻则连不上重则带来安全隐患。我在本地开发时习惯单独创建一个业务数据库再单独创建一个只能在本机访问的账号这样产生的配置在迁移到生产时只需要换主机地址、密码和 SSL 参数代码逻辑不用动。这一步听起来没有必要但等你项目变大、团队里多几个人一起开发时就知道独立账号、独立库有多重要了。2. 环境准备JDK、MySQL 8、可视化客户端怎么搭最省事2.1 版本选型JDK、Spring Boot、MySQL 三者怎么搭配我经常看到有人拿着很新的 Spring Boot 3 项目却还在用 JDK 8 编译结果一堆报错。这里先明确一个硬性标准Spring Boot 3.x 强制要求 JDK 17 以上而且包名从javax换成了jakarta。如果电脑上只有 JDK 8就别硬上 Spring Boot 3老老实实用 Spring Boot 2.7.18。版本搭配建议参考下面这张表使用场景JDKSpring BootMySQL说明维护老项目JDK 82.7.185.7 / 8.0兼容性好资料多适合低版本环境新项目首选JDK 173.2.x8.0长期维护安全更新及时本地快速验证JDK 173.2.x8.0配合 MySQL 8 官方驱动即可MySQL 5.7 和 8.0 的默认认证插件不一样5.7 是mysql_native_password8.0 是caching_sha2_password。如果你用旧驱动连 MySQL 8会出现认证插件不兼容的报错。解决方法是换新的com.mysql.cj.jdbc.Driver不要只看教程把老写法抄下来。2.2 MySQL 8 本地安装与初始化要点官方下载安装包这个环节本身不难难的是安装之后要记得做两件小事设置字符集和记录初始化密码。以 Windows 解压版为例下载mysql-8.0.x-winx64.zip后解压到比如 C 盘某个目录。建议在根目录下创建my.ini至少把字符集和端口固定下来[mysqld] basedirC:/mysql-8.0.36-winx64 datadirC:/mysql-8.0.36-winx64/data port3306 character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci default_authentication_pluginmysql_native_password [mysql] default-character-setutf8mb4然后以管理员身份打开命令行执行初始化命令mysqld --initialize --console这一步会在控制台打印一个临时 root 密码一定要复制保存下来后面登录时要用。接着执行mysqld --install net start mysql服务启动成功后用临时密码登录mysql -uroot -p首次登录后尽快改掉 root 密码我建议直接创建一个专用开发账号而不是拿 root 到处用。macOS 用户用 Homebrew 安装更省事Linux 用户用 apt 或 dnf 也可以但无论哪种方式初始化之后的第一件事都是确认端口、字符集、认证方式这三点。2.3 创建测试数据库与专用账号登录 MySQL 后先把默认字符集固化到库级别这样即使代码里漏配字符集参数中文也不会变成乱码CREATE DATABASE demo_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER demo_userlocalhost IDENTIFIED BY Demo123456; GRANT ALL PRIVILEGES ON demo_db.* TO demo_userlocalhost; FLUSH PRIVILEGES;需要说明的是权限精确到demo_db而不是*.是刻意为之。后面如果不想让别人看到其他库这个权限模型就能直接复用不用再改结构。搞定之后使用 Navicat、MySQL Workbench 或命令行随便哪个客户端用demo_user去连demo_db能连上就说明数据库侧环境已经就绪。3. 项目创建与配置一份能直接跑的 application.yml 是怎么来的3.1 用 IDEA 从 Spring Initializr 建项目本地项目不用搞太复杂的构建方式直接用 IDEA 新建 Spring Boot 项目就行。在 Spring Initializr 界面里Java 版本选 17然后勾选这三个依赖Spring Web提供 REST 接口能力Spring Data JPA负责对象关系映射和仓库接口MySQL Driver负责和 MySQL 服务通信用这种方式创建出来的项目自带一个可启动的主类。之后只要把配置写对就能跑通连接。如果你更习惯用 MyBatis可以把 Spring Data JPA 换成 MyBatis 相关依赖后面我会专门说一下差异。创建一个/tmp/mysql表用于演示不过现在先不急着建等代码写完再一起看效果。项目创建好后pom.xml里的依赖片段大概是这样的dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependencySpring Boot 3 里官方把驱动坐标改成了com.mysql:mysql-connector-j不少老教程里写的mysql:mysql-connector-java已经不推荐了。如果你发现自己项目里驱动加载不到先看一下是不是用了旧坐标。3.2 application.yml 配置里的每一项都有用Spring Boot 的配置文件既可以用application.properties也可以用application.yml。我个人更推荐 yml因为层次关系清楚数据库这种多级配置不会写成一长串。下面是一份实测能跑的完整配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/demo_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: demo_user password: Demo123456 driver-class-name: com.mysql.cj.jdbc.Driver hikari: minimum-idle: 2 maximum-pool-size: 10 connection-timeout: 30000 max-lifetime: 1200000 jpa: hibernate: ddl-auto: update show-sql: true properties: hibernate: format_sql: true先解释url这一段。jdbc:mysql://localhost:3306/demo_db说明数据库在本地 3306 端口库名是demo_db。后面的参数里useUnicodetruecharacterEncodingutf8请求使用 UTF-8 编码配合数据库的utf8mb4中文才稳。useSSLfalse本地开发时禁用 SSL减少握手阶段的报错。serverTimezoneAsia/Shanghai统一时区避免 MySQL 驱动和本地系统时区不一致。allowPublicKeyRetrievaltrue连接 MySQL 8 时允许获取公钥解决认证插件验证问题。driver-class-name推荐写com.mysql.cj.jdbc.Driver。老的com.mysql.jdbc.Driver在 MySQL 8 中虽然兼容但会有过时警告直接写新的更干净。3.3 连接池、自动装配和 Driver 的那点关系Spring Boot 项目默认带 HikariCP 连接池。它的工作原理是启动时预创建一批空闲连接请求来了直接复用避免每次操作数据库都经历建连、认证、释放的过程。自动装配由DataSourceAutoConfiguration完成它会读取spring.datasource.*配置然后创建HikariDataSource实例。连接池参数不是越大越好。本地开发机器和单库场景下maximum-pool-size设在 10 左右足够minimum-idle可以保持 2。max-lifetime指的是连接最大存活时间建议小于 MySQL 的wait_timeout不让数据库端把空闲连接断开后客户端还在傻等。这段配置虽然不直接产出业务功能却决定了高并发下的连接等待体验。4. 把 MySQL 真正操作起来用 JPA 实现一套增删改查4.1 建表、实体类映射先给数据库准备一张学生表用于演示本地数据读写USE demo_db; CREATE TABLE student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, age INT NOT NULL, major VARCHAR(100) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;然后创建实体类。Spring Boot 3 JPA 里实体注解用的是jakarta.persistence包package com.example.demo.entity; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; Entity Table(name student) public class Student { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String name; private Integer age; private String major; public Student() { } public Long getId() { return id; } public void setId(Long id) { this.id id; } public String getName() { return name; } public void setName(String name) { this.name name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age age; } public String getMajor() { return major; } public void setMajor(String major) { this.major major; } }这里有个细节Java 实体里的字段命名用驼峰数据库列用下划线JPA 的命名策略会默认把createTime映射为create_time。如果你自己写Column(name create_time)那就以注解为准不发生自动转换。4.2 Repository 与 Controller少写很多样板代码Spring Data JPA 的核心价值在于你只要定义一个继承JpaRepository的接口不用写实现类框架就会自动实现基本增删改查方法。查询方法也可以根据方法名自动生成 SQL比如findByAgeBetween会自动翻译成WHERE age BETWEEN ? AND ?package com.example.demo.repository; import com.example.demo.entity.Student; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; public interface StudentRepository extends JpaRepositoryStudent, Long { ListStudent findByAgeBetween(Integer min, Integer max); }接着写一个 Controller用来提供 REST 接口验证本地数据库读写package com.example.demo.controller; import com.example.demo.entity.Student; import com.example.demo.repository.StudentRepository; import org.springframework.web.bind.annotation.*; import java.util.List; RestController RequestMapping(/students) public class StudentController { private final StudentRepository studentRepository; public StudentController(StudentRepository studentRepository) { this.studentRepository studentRepository; } GetMapping public ListStudent list() { return studentRepository.findAll(); } GetMapping(/{id}) public Student one(PathVariable Long id) { return studentRepository.findById(id).orElseThrow(); } PostMapping public Student create(RequestBody Student student) { return studentRepository.save(student); } PutMapping(/{id}) public Student update(PathVariable Long id, RequestBody Student student) { return studentRepository.findById(id) .map(existing - { existing.setName(student.getName()); existing.setAge(student.getAge()); existing.setMajor(student.getMajor()); return studentRepository.save(existing); }) .orElseThrow(); } DeleteMapping(/{id}) public void delete(PathVariable Long id) { studentRepository.deleteById(id); } }控制器代码写得很薄业务直接落在 Repository 上本地演示足够。实际项目到了后期我会把“取数据、做校验、改状态、落库”这些逻辑抽到 Service 层Controller 里保持温和。4.3 用 curl 走一遍请求并回头查库项目启动之后控制台出现 Tomcat started 的日志说明 Spring Boot 已经起来。此时用 curl 模拟客户端请求顺序验证增删改查。新增一条数据curl -X POST http://localhost:8080/students \ -H Content-Type: application/json \ -d {name:张三,age:21,major:软件工程}查询全部curl http://localhost:8080/students不出意外返回的 JSON 里会出现刚才新增的记录而且数据库里student表也多了对应的一行。这就是本地连接成功最直观的证据。接下来再验证更新和删除curl -X PUT http://localhost:8080/students/1 \ -H Content-Type: application/json \ -d {name:李四,age:22,major:大数据} curl -X DELETE http://localhost:8080/students/1演示完后可以回数据库执行SELECT * FROM student;看看数据是否真的发生变化。这一步看起来像是在绕路但实际操作中有很多人接口返回成功数据库却是空的原因往往是用了别的库或者测试数据写进了内存数据库。回到原库去查才能确定操作的就是我们希望的库表。4.4 换用 MyBatis、MyBatis-Plus 或 JdbcTemplate 时要注意什么JPA 不是本地连接 MySQL 的唯一途径。下面这张表是不同方案在本地开发中的实际表现方案优点适合场景Spring Data JPA单表 CRUD 代码量少自动装配方便快速原型、简单的管理后台MyBatisSQL 全自动复杂查询可读性好擅长报表、统计类查询MyBatis-Plus继承基础 CRUD条件构造器好用业务系统、单表操作占比高JdbcTemplate轻量无 ORM 概念追求极致控制、连接池复用不管用哪种组合数据源配置部分完全一样。区别只是在依赖和 Mapper 注解上spring.datasource.url、username、password不会被影响。这正是 Spring Boot 设计的巧妙之处数据库连接是一层独立能力业务选型无论 JPA 还是 MyBatis都可以共用同一份数据源配置。5. 本地连接 MySQL 最常见的 5 类问题5.1 SSL 错误与认证插件不兼容现象一启动项目时报SSL connection error或Unable to load authentication plugin caching_sha2_password。本质是 MySQL 8 默认用的认证插件和驱动侧的 SSL 策略不一致。本地开发时最简单的处理是在 URL 后追加两个参数useSSLfalseallowPublicKeyRetrievaltrue这能解决本地约八成的连接失败。需要明确useSSLfalse只适用于本地开发生产环境如果数据库支持 SSL应该反过来开启 SSL 并配置证书而不是图省事继续关闭。有一种更绕的坑项目里引了mysql-connector-java5.x 老版本驱动这版不支持 MySQL 8 的认证方式。改成 Spring Boot 3 默认管理的com.mysql:mysql-connector-j后问题会直接消失。5.2 “Public Key Retrieval is not allowed” 是怎么回事这个报错几乎只在 MySQL 8 上出现非常典型。原因是caching_sha2_password算法在内网连接时需要向服务器请求公钥来加密密码。如果allowPublicKeyRetrieval没开驱动就不允许获取公钥连接自然失败。解决方案有两个。一是加上allowPublicKeyRetrievaltrue本地用最简单。二是把用户的认证插件改回mysql_native_passwordALTER USER demo_userlocalhost IDENTIFIED WITH mysql_native_password BY Demo123456; FLUSH PRIVILEGES;从实用角度讲我会优先选第一种因为改认证插件属于数据库层面的全局动作可能会影响其他客户端没有必要为一个连接去改服务端默认行为。5.3 时区、字符集和乱码报错最常见的这种The server time zone value ... is unrecognized or represents more than one time zone.原因很简单MySQL 服务器时区和运行 Spring Boot 的 JVM 时区不一致。追加serverTimezoneAsia/Shanghai即可。想彻底点也可以在 MySQL 端执行SET global time_zone 8:00;双管齐下。乱码问题则通常是字符集不一致。常见情况是数据库默认latin1而实体类往里面写中文结果落到库里全变成问号。最佳实践是数据库、表、连接 URL 三处都用utf8mb4。utf8mb4比utf8多支持四字节字符未来遇到 emoji 也不会出岔子。5.4 驱动、URL 与 Access denied这类问题通常直接体现在异常前几行容易定位异常关键字原因解决方法ClassNotFoundException缺少驱动依赖或坐标写错换成com.mysql:mysql-connector-jUnknown databaseURL 里的库名不存在在 MySQL 里创建同名数据库Access denied for user用户名密码错误或权限不足检查密码、账号是否只授权了指定库Communications link failure服务没启动或端口不可达netstat -ano这里特别提醒一句Access denied 不一定是密码错。我遇到过两次都是因为账号权限只授权了其他人的库业务库里根本没权限。所以排查时先执行SHOW GRANTS FOR demo_userlocalhost;看看账号到底能碰哪些库。5.5 MySQL 服务没起来或者端口被占用如果项目启动时直接报Connection refused先把 MySQL 服务状态确认一遍。Windows 上可以用net start MySQL或任务管理器查看服务macOS 和 Linux 用service mysql status或systemctl status mysql。端口占用也是本地开发常见的坑。360 或某些全家桶软件喜欢占用 3306或者你自己开了多个 MySQL 实例导致端口冲突。执行下面这条命令能立刻看到是谁占用了 3306netstat -ano | findstr 3306如果发现占用进程不是 mysqld要么停掉该进程要么把 MySQL 端口改成 3307同时把 URL 里的端口也同步改掉。端口这东西只要两处一致就不存在“对不上”的问题。6. 连接成功后的几个本地开发好习惯6.1 全链路字符集对齐数据库、连接、实体一处不漏中文乱码的根源十有八九是字符集不一致。数据库是utf8mb4连接 URL 也要带上useUnicodetruecharacterEncodingutf8而 Maven 项目里的源码文件编码最好在pom.xml里设置成 UTF-8properties project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties这套链路看着繁琐但能让你在本地开发时彻底告别“页面全是问号”的情况。相比之下任何一环缺失都可能让人花半小时去排查一个根本不复杂的编码问题。6.2 连接池生命周期要与 MySQL 超时匹配MySQL 服务端默认wait_timeout一般是 8 小时也就是说连接空闲超过这个阈值会被服务端主动断开。如果连接池里的连接存活时间超过 8 小时某次请求就会拿到失效连接出现“用着用着突然报连接关闭”的情况。本地开发和生产环境都要遵守一个规则连接池的max-lifetime比 MySQL 的wait_timeout小一点。比如 MySQL 的wait_timeout是 28800 秒HikariCP 的max-lifetime可以配成 1200000 毫秒也就是 20 分钟这比 8 小时小得多完全能避免拿到已断开的连接。想确认 MySQL 端真实超时时间执行SHOW VARIABLES LIKE wait_timeout;这个命令在生产排查时同样适用属于数据库优化里很基础但必须了解的点。6.3 用日志和客户端观察 SQL 执行show-sql: true能帮你在控制台看到 JPA 自动生成的 SQL。注意show-sql因为使用stdout输出启动时可能打印 SQL但也容易刷屏。实际项目我更推荐用logging.level.org.hibernate.SQLdebug这样 SQL 可以统一进日志文件不污染控制台。本地调试时我会同时开着 Navicat 或 MySQL Workbench 的会话监控。这样当 Controller 收到请求后数据库客户端能立刻看到哪条表被加了锁、哪个连接被占住。有一次我发现接口慢到十几秒排查后不是 SQL 慢而是锁等待时间过长这种情况只靠业务代码看很难定位数据库端会话列表一打开就一目了然。再补充一个成本很低但很实用的习惯每次本地调试完数据不要手动在客户端里乱改表尽量通过接口或明确的 SQL 脚本来操作。这样项目在换人接手时只需要跑一遍初始化脚本就能复现全流程不必靠人工记忆“当初往库里塞了哪些测试数据”。这个习惯对长期维护的帮助比想象中的大得多。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。