尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Spring Boot本地连接MySQL实战:配置、三种操作方式与排错指南

Spring Boot本地连接MySQL实战:配置、三种操作方式与排错指南 1. 搞清楚本地连接MySQL这件事到底卡在哪很多人第一次在Spring Boot项目里连MySQL遇到的情况都差不多照着别人的博客敲了一遍配置mvn spring-boot:run一启动日志里刷出一大串红字——Access denied、Communications link failure、Public Key Retrieval is not allowed心态直接崩掉。我记得有次帮同事排查一个本地项目他反复确认账号密码没错但就是连不上。最后发现MySQL服务根本没启动他以为装完MySQL就等于服务一直在跑。这类问题其实特别普遍因为“本地连接并操作MySQL”看起来是Spring Boot的配置问题但实际是一条完整的链路MySQL要装好、服务要启动、库要建好、账号要有权限、驱动要匹配、URL参数要对、连接池要正常初始化最后才是代码层面去读写数据。这篇文章就把这条链路完整走一遍。我会从最基础的MySQL本地环境准备开始讲到Spring Boot 2.7/3.x版本下的配置方式再给三种主流操作MySQL的方案JdbcTemplate、Spring Data JPA、MyBatis最后整理一份高频报错的排查清单。无论你是刚入门Java后端还是做毕设、做练手项目照着这篇文章一步步来基本能解决90%的“本地连不上MySQL”问题。2. 先把手里的MySQL准备好2.1 本地MySQL安装版本选择与初始化Spring Boot对MySQL版本并没有严格的绑定关系但实际使用中MySQL 5.7和MySQL 8.0是最常见的两个大版本。如果你是新装我建议直接用MySQL 8.0原因很简单性能更好、默认字符集是utf8mb4、支持窗口函数等现代SQL特性。但如果你的项目是老项目或者同事统一在用5.7那就保持一致避免开发环境和生产环境行为不一致。安装包去哪里下这是个很多人会问的问题。我一般直接去MySQL官网的Community Downloads页面选MySQL Community Server然后根据操作系统下载对应的安装包。Windows下选MSI InstallermacOS可以选DMG或者用HomebrewLinux的话用apt或yum装也行。注意官网下载页可能跳转到CDN下载速度慢的话可以选国内的镜像源这个就看个人情况了。Windows安装时有一个比较关键的节点设置root密码。很多人习惯随手设一个简单的密码比如root或123456本地开发可以这样但建议至少记清楚。另外安装类型里如果选了“Server only”就不会装客户端工具后续命令行操作可能会不方便。如果你不太熟悉终端操作可以在安装时把MySQL Workbench一起装好它是一个图形化工具能看到数据库列表、执行SQL、查看表结构排错的时候非常有用。macOS用户用Homebrew的话命令很简单brew install mysql brew services start mysql刚装完的MySQL root账号默认没有密码直接用mysql -u root就能进。装完之后的安全初始化mysql_secure_installation我建议做一下它会帮你设置密码、删除匿名用户、禁止root远程登录本地开发环境做一遍也没坏处。2.2 建库、建账号、给权限Spring Boot连接MySQL时用的是“账号密码指定数据库”的方式。很多人犯的一个错误是直接用root账号连接项目专用的数据库。这在本地开发问题不大但不是一个好习惯。更规范的做法是每个项目单独建一个账号只给这个账号访问对应库的权限这样以后项目多了互不影响也不至于一个root账号处处裸奔。在MySQL命令行或Workbench里执行CREATE DATABASE demo_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER demo_userlocalhost IDENTIFIED BY demo_pass; GRANT ALL PRIVILEGES ON demo_db.* TO demo_userlocalhost; FLUSH PRIVILEGES;解释一下这几条语句demo_db是项目要用的数据库字符集用utf8mb4因为现在几乎都要存Emoji或生僻字utf8mb4覆盖范围最全兼容最好的就是它。demo_user是Spring Boot连接时用的账号localhost表示只允许从本机连接。GRANT ALL PRIVILEGES ON demo_db.*表示这个账号对这个库下所有表有全部操作权限但对其他库没有任何权限。FLUSH PRIVILEGES是刷新权限缓存让用户权限立即生效本地配置后最好执行一下。做完这一步你可以先手动在MySQL里建一张测试表比如USE demo_db; CREATE TABLE user_info ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, email VARCHAR(100), created_at DATETIME DEFAULT CURRENT_TIMESTAMP );这张表后面会用来验证Spring Boot的CRUD操作是否生效。2.3 理解JDBC连接URL的组成Spring Boot连MySQL本质上是JDBCJava Database Connectivity在做底层通信。JDBC驱动通过一个统一资源定位符URL去找到MySQL服务这个URL的格式是jdbc:mysql://主机地址:端口号/数据库名?参数1值1参数2值2本地开发时主机地址通常是127.0.0.1或localhost端口默认是3306。重点在于后面跟的参数这些参数决定了驱动如何跟MySQL握手。比如你可能会看到这样的连接串jdbc:mysql://localhost:3306/demo_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue这里每个参数都有它的作用useUnicodetruecharacterEncodingutf8使用Unicode字符集并按UTF-8编码传输避免中文乱码。serverTimezoneAsia/Shanghai指定服务器时区。MySQL 8.0默认时区是UTC如果不指定驱动拿到的时间会和你本地时间差8个小时。useSSLfalse本地开发不启用SSL加密连接。启用SSL会带来额外的握手开销本地无必要。allowPublicKeyRetrievaltrue这个参数专门配合MySQL 8.0的caching_sha2_password认证方式使用不加上去会报“Public Key Retrieval is not allowed”。理解了这个URL你就知道为什么别人贴的配置照抄也可能出错——因为库名、端口、时区、参数可能跟你的环境不一样。3. Spring Boot项目里的核心配置3.1 pom.xml依赖驱动包名和版本有讲究Spring Boot项目接入MySQL首先要在pom.xml里加两个依赖一个是Spring Boot的JDBC或JPA/MyBatis启动器另一个是MySQL驱动。最基础的方式是加spring-boot-starter-jdbc和MySQL驱动dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency注意这里有个细节新版本的MySQL驱动groupId和artifactId都有变化。老版本是mysql:mysql-connector-java从8.0.31之后官方把artifactId改成了mysql-connector-j。Spring Boot 2.7.x和3.x的依赖管理已经默认管理了这个新坐标所以你在pom里不写版本号也能正确解析。这也是“Springboot版本太高”这个热搜词背后的一个坑如果你的Spring Boot版本比较高比如3.x用老的mysql-connector-java坐标可能解析不到对应版本或者驱动包版本落后导致兼容问题。最好的做法就是直接用新坐标让Spring Boot的依赖管理帮我们控制版本。另外如果你是做Spring Data JPA或MyBatis引入的starter会间接引入spring-boot-starter-jdbc所以只需要再加对应的starter即可不需要重复加JDBC的依赖。3.2 application.yml配置从0到能连Spring Boot的配置写在src/main/resources/application.yml里。一个完整的本地MySQL配置如下spring: datasource: url: jdbc:mysql://localhost:3306/demo_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: demo_user password: demo_pass driver-class-name: com.mysql.cj.jdbc.Driver解释几个点driver-class-nameMySQL 8.0驱动类的完整类名是com.mysql.cj.jdbc.Driver老版本是com.mysql.jdbc.Driver。如果你用MySQL 8.0驱动写了老的类名会出现提示让你换成新的。url里的数据库名、账号密码要和你在MySQL里建的保持一致。有些教程会写spring.datasource.druid.url这类配置那是引入了Druid连接池之后的写法。只引入Spring Boot默认的JDBC时就是spring.datasource.url。如果你检查过Spring Boot自动配置的源码会发现DataSourceProperties类就是这个前缀。Spring Boot通过DataSourceAutoConfiguration自动识别你配置的URL然后选择一个DataSource实现默认是HikariCP连接池。3.3 连接池参数默认HikariCP怎么调Spring Boot 2.x之后默认的连接池是HikariCP这个选择不是拍脑袋决定的。HikariCP在性能测试里表现非常稳定连接获取速度快内存占用低。你不需要理解它内部怎么实现的只需要知道默认情况下它就是Spring Boot的连接池方案而且它很靠谱。但是在本地开发时默认参数并不一定舒服。HikariCP默认的maximum-pool-size是10minimum-idle是10connection-timeout是30000ms。本地开发如果只是为了调试API10个连接完全够用但如果你启动了多个服务实例或者用Docker跑了一些其他组件可以考虑把池子调小一点spring: datasource: hikari: minimum-idle: 2 maximum-pool-size: 5 connection-timeout: 3000 idle-timeout: 600000这里每个参数的含义minimum-idle连接池维护的最小空闲连接数。maximum-pool-size连接池最大连接数。connection-timeout获取连接的超时时间本地开发网络快3秒足够了不用傻等30秒。idle-timeout空闲连接存活时间超过这个时间没使用的连接会被回收。调这些参数不用太纠结“最佳值”因为不同项目对数据库的压力完全不一样。本地开发优先保证不报错、不拖慢启动线上再根据压测数据去调整。3.4 Spring Boot 2.x和3.x的差异这里值得单独说一嘴。Spring Boot 3.x最低要求JDK 17而且底层的Jakarta EE命名空间从javax.*迁移到了jakarta.*。这意味着如果你引入了一些老版本的第三方库它们内部用了javax.servlet这类包名在3.x下直接编译不过或运行报错。如果你在用JDK 8或者JDK 11那Spring Boot 2.7.x是最后的可用版本这也对应了热词里“idea不能创建springboot项目不能使用jdk1.8”的情况。新创建的Spring Boot项目默认可能是3.x但老环境的JDK版本不支持。这种情况下要么升级JDK到17要么在创建项目时手动选择Spring Boot 2.7.x的版本。无论哪个大版本本文讲的MySQL连接配置方式是完全一致的只是依赖坐标和SDK版本有区别。4. 三种方式操作MySQL按场景选4.1 轻量方案JdbcTemplate如果你项目的SQL比较复杂或者你习惯手写SQLJdbcTemplate是成本最低的入门选择。它不需要额外引入ORM框架Spring Boot的spring-boot-starter-jdbc已经把它包含在内了。使用方式很简单在Service里注入JdbcTemplateService public class UserService { private final JdbcTemplate jdbcTemplate; public UserService(JdbcTemplate jdbcTemplate) { this.jdbcTemplate jdbcTemplate; } public ListMapString, Object findAllUsers() { return jdbcTemplate.queryForList(SELECT * FROM user_info); } public int insertUser(String username, String email) { return jdbcTemplate.update( INSERT INTO user_info(username, email) VALUES (?, ?), username, email ); } }这里有两个地方值得注意。第一JdbcTemplate使用?占位符传参不要用字符串拼接SQL。用?有两个好处避免SQL注入同时让数据库能复用执行计划。字符串拼接的话用户输入里如果带了个 OR 11SQL语义就变了这是最经典的安全漏洞。第二update方法返回的是受影响行数你可以根据这个返回值判断是否插入成功。queryForList返回的是ListMapString, Object每一行是一个Map键是列名值是列值。实际项目里用JdbcTemplate的最大感受是“直接、透明”。你看到什么SQL执行的就是什么SQL没有ORM的隐式查询排查问题特别省心。缺点也很明显所有字段映射都要手写表多了以后代码量上去可维护性会下降。4.2 数据模型方案Spring Data JPASpring Data JPA适合业务模型清晰、CRUD为主的系统。它通过实体类映射数据库表Repository接口的方法名自动生成SQL写起来几乎是“零SQL”。先引入依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency再定义一个实体类和user_info表对应Entity Table(name user_info) public class User { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String username; private String email; Column(name created_at, insertable false, updatable false) private LocalDateTime createdAt; // 无参构造、Getter/Setter 省略 }然后是Repository接口public interface UserRepository extends JpaRepositoryUser, Long { ListUser findByUsername(String username); ListUser findByEmailContaining(String keyword); }这里findByUsername、findByEmailContaining就是Spring Data的方法命名规范框架会根据方法名自动生成对应的查询SQL。你不需要写任何实现类Spring会在运行时帮你生成代理对象。使用JPA时需要注意两个点。一个是GenerationType.IDENTITY对应MySQL的自增主键。如果你用GenerationType.AUTOHibernate可能生成hibernate_sequence这张表和MySQL的习惯不一致容易把表结构搞乱。另一个是实体类和表的映射。列名如果和MySQL的命名习惯不一样比如数据库里是created_at下划线风格实体类里是createdAt驼峰风格需要加Column(name created_at)来显式指定。Spring Boot默认的spring.jpa.hibernate.ddl-auto在本地可能是update或create-drop我建议尽量用none或validate把表结构变更用数据库迁移工具管理不要让Hibernate自动改表结构否则线上出问题很难排查。4.3 国内主流方案MyBatisMyBatis在Java后端占有率一直很高原因是它保留了SQL的灵活性同时提供了Java和SQL之间的映射能力。Spring Boot整合MyBatis时Spring Boot官方不直接管理MyBatis的依赖你需要引入MyBatis团队提供的启动器dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.2/version /dependency如果用的是Spring Boot 3.x需要选一个兼容版本。MyBatis启动器从2.3.x开始适配Spring Boot 3.x2.3.2实测在Spring Boot 3.2下是可以正常工作的。使用方式分两种注解写SQL或者XML写SQL。注解方式适合简单查询Mapper public interface UserMapper { Select(SELECT * FROM user_info WHERE username #{username}) User findByUsername(String username); Insert(INSERT INTO user_info(username, email) VALUES (#{username}, #{email})) int insert(User user); }XML方式适合复杂动态SQL?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.demo.mapper.UserMapper select idfindPage resultTypecom.example.demo.entity.User SELECT id, username, email, created_at FROM user_info where if testkeyword ! null and keyword ! AND username LIKE CONCAT(%, #{keyword}, %) /if /where ORDER BY id DESC LIMIT #{offset}, #{limit} /select /mapperXML方式还有一个配套的关键配置在application.yml里指定mapper XML的位置和实体类别名的包路径mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.demo.entitytype-aliases-package可以让XML的resultType直接写类名不用写全限定名。MyBatis这里最容易犯的错是#{}和${}混用。#{}是预编译占位符会生成?参数安全且推荐${}是字符串拼接直接把值拼进SQL只有在你确实需要动态改变表名、列名等结构时才用。日常查询一律用#{}。4.4 分页、事务这些绕不开的操作分页是Web系统最常用的功能之一MyBatis时代的首选分页方案是PageHelper。引入依赖dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper-spring-boot-starter/artifactId version1.4.7/version /dependency使用方式极为简洁在查询前调用PageHelper.startPage(pageNum, pageSize)紧接着执行的那条查询语句会被自动分页public PageInfoUser getUserPage(int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); ListUser users userMapper.findAll(); return new PageInfo(users); }返回的PageInfo里包含了总记录数、总页数、当前页数据等完整信息。注意startPage必须紧跟要分页的查询方法中间不能再调用其他Mapper方法否则分页会作用到错误的查询上。事务处理同样重要。Spring Boot里你只需要在一个方法上标注TransactionalTransactional public void createUserWithProfile(User user) { userMapper.insert(user); userProfileMapper.insert(user.getProfile()); }这个方法内所有的数据库操作要么全部成功要么全部回滚。比如第二个插入失败第一个插入也会被撤销数据不会出现半截状态。默认只回滚RuntimeException和Error如果你要捕获CheckedException并回滚需要在注解里声明Transactional(rollbackFor Exception.class)。5. 本地连MySQL的翻车现场与排查清单5.1 连接失败类错误从报错第一行开始查本地连接MySQL最常见的坑我整理成一张速查表报错信息原因解决办法Access denied for user demo_userlocalhost账号密码错误或账号没有该库权限检查密码用GRANT重新授权Public Key Retrieval is not allowedMySQL 8.0认证方式与参数缺失URL加allowPublicKeyRetrievaltrueThe server time zone value Öйú±ê׼ʱ¼ä时区未指定且乱码URL加serverTimezoneAsia/ShanghaiUnknown database demo_db数据库不存在先执行CREATE DATABASECommunications link failureMySQL服务未启动、端口占用或网络不通启动MySQL检查3306端口java.sql.SQLNonTransientConnectionException驱动和MySQL版本不匹配排查驱动版本、Driver类名遇到报错优先看日志里最靠前的异常堆栈那才是根因。很多人容易犯的错是盯着最后一行看那个位置往往是结果信息真正的原因在“Caused by”里。5.2 时区、SSL、公钥获取的连环坑这三个问题在MySQL 8.0下特别常见而且经常一起出现。时区问题MySQL 8.0默认时区是UTC如果你的电脑在东八区驱动拿到的时间就等于你当前时间减去8小时。解决办法就是在URL里加serverTimezoneAsia/Shanghai。SSL问题JDBC驱动的旧版本默认对连接做SSL握手本地开发没有配置证书时控制台会刷一片SSL报错日志。加上useSSLfalse直接关掉本地不涉及明文传输风险没必要握手。公钥获取问题MySQL 8.0默认创建的用户用caching_sha2_password认证第一次连接时客户端需要向服务器获取RSA公钥来加密密码传输。如果你没配置SSL驱动默认不允许自动获取公钥于是报Public Key Retrieval is not allowed。加上allowPublicKeyRetrievaltrue允许获取即可。这三个参数我强烈建议本地连接时全部写上一次性省掉几个小时的排查时间。我自己的配置文件里长期保留这三件套?serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue5.3 关于账号权限、服务启动的检查小技巧如果连接还是失败不要急着改代码按下面的顺序检查用MySQL命令行工具先测试一下账号能不能登录。命令行能进说明MySQL层面没问题问题出在Spring Boot配置或网络。检查localhost和127.0.0.1的区别。部分系统里localhost和127.0.0.1是两个不同的账号记录如果你的用户只授权了localhost但JDBC URL里用的是127.0.0.1也会授权失败。云端或容器环境尤其容易出现。检查端口。netstat -an | grep 3306Windows是netstat -ano | findstr 3306看一下3306端口是否处于监听状态。如果没监听MySQL服务没启动。检查MySQL配置文件里的bind-address。MySQL默认只能本机连接一般足够如果改了绑定0.0.0.0任何机器都能尝试连你的MySQL本地开发建议保持默认。5.4 线上数据与本地表的差异问题还有一个很隐蔽但常见的问题同一份启动配置在不同机器上表现不一致。比如同事的电脑能跑通你的电脑报错这一步有时候是MySQL版本不一致导致的。最常见的差异就是MySQL 5.7默认认证插件是mysql_native_passwordMySQL 8.0默认是caching_sha2_password。如果你用5.7的驱动去连8.0的库会提示Unable to load authentication plugin caching_sha2_password。反之用8.0的驱动去连5.7的库一般没问题因为驱动是向后兼容的。所以我的建议是锁定MySQL版本和驱动版本最好用Docker或同一份安装包来统一环境。本地开发图省事的话直接都用MySQL 8.0 最新驱动少很多麻烦。6. 实践中的几点总结与经验写到这里本地连接并操作MySQL这条路已经完整走了一遍。我自己做过很多Spring Boot项目接触过的团队成员也不少总结一下个人体会。**先把MySQL层搞定再动Spring Boot配置。**很多人遇到连接错误第一反应是改application.yml但问题其实出在MySQL账号权限或服务状态上。用命令行工具验证一遍MySQL层没问题了再回头看Spring Boot。这个习惯能省下大量时间。**连接URL参数不要删减。**本地开发时serverTimezone、useSSL、allowPublicKeyRetrieval这三个参数我建议一直保留。“看似没必要”的参数往往就是报错的根源尤其是MySQL 8.0环境。你可以在项目里统一维护一份推荐的URL模板新项目到手直接复制别每次重新敲。**尽量用连接池和预编译SQL。**JDBC层面预编译占位符是防SQL注入的底线页面查询也尽量用PageHelper或Spring Data的分页能力。它们不仅让代码更整洁也避免了自己写OFFSET/LIMIT时把边界算错。**最后分享一个小技巧。**Spring Boot项目启动的时候如果你不确定数据源是否配置正确可以在测试类里注入DataSource并打印连接信息SpringBootTest class DemoApplicationTests { Test void testConnection() { try (Connection conn dataSource.getConnection()) { System.out.println(connected: conn.getCatalog()); } catch (SQLException e) { e.printStackTrace(); } } }能打印出connected: demo_db说明整条链路是通的之后再写CRUD代码调试起来会顺畅很多。
返回列表