news 2026/10/3 3:24:02

SpringBoot+MyBatis+MySQL整合实战:版本选型、配置避坑与CRUD完整教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+MyBatis+MySQL整合实战:版本选型、配置避坑与CRUD完整教程

先交代一个背景:后台经常有人问我同一个问题——在IDEA里搭一套SpringBoot+MyBatis+MySQL的工程,明明照着网上的教程一步步来,pom.xml该写的依赖都写了,application.yml也没落下,但工程就是起不来。要么是启动报错,要么是数据库连接不通,要么是写好的Mapper方法调不动。其实这些翻车现场十有八九不是代码本身有什么问题,而是版本匹配关系没理清楚,或者配置文件里少了一两个关键参数。这篇就把我从零到一搭建的完整过程摊开写,包括版本选型、每一步配置背后的理由、实测踩过的坑,以及最后能直接跑通的CRUD代码。适合刚接触SpringBoot没多久、想尽快把MyBatis和MySQL塞进工程里的新人,也适合被各种“怪病”折磨到想卸载IDEA的老手来抄作业。

1. 项目概述与环境准备

1.1 这个项目到底在解决什么问题

先说穿这个项目本质上是干什么的:SpringBoot负责把整个Web应用的骨架撑起来,内置Tomcat、自动配置、依赖管理;MyBatis负责把Java方法和SQL语句做映射,让你不用费劲写一堆JDBC的样板代码;MySQL则是真正把数据落盘的地方。三个角色串起来的完整调用链是:浏览器请求打到Controller,Controller把请求交给Service做业务处理,Service调用Mapper接口,MyBatis根据Mapper接口找到对应的XML里的SQL,SQL在MySQL上执行,结果再原路返回。

理解了这条链路,后续排查问题就能很快定位。比如“接口502返回了什么”不用慌,要么是Controller没接收到请求,要么是Service里抛了异常,要么是SQL写错,一层层往下翻就行。

这类工程最常见的几个应用场景,我简单列一下:

  • 学校里的课程设计或者毕业设计,要求做一个带增删改查的管理系统,这是最典型的场景。
  • 公司内部的一些小工具、后台管理页面,数据量不大但要能快速迭代,SpringBoot+MyBatis就非常顺手。
  • 个人练手的博客、记账本、待办事项之类的项目,架构简单好维护。
  • 把一些老的JavaWeb工程(SSH、Servlet+JSP那种)往新框架迁移,很多第一步就是先把数据访问层换成MyBatis。

不管哪一种情况,这个“最小可运行”的工程搭建都是第一步,先把路跑通,后面加什么缓存、消息队列、对象存储,都是在同一套骨架上面生长出来的。

1.2 版本选型:最容易被忽略的坑

我见过太多人项目起不来,最后发现是版本瞎配。版本这块是真真切切的经验教训,我快速总结一下当前最稳的选型建议。

组件推荐版本替代选择说明
JDKJDK 8 或 17JDK 11SpringBoot 2.x用JDK8,SpringBoot 3.x必须用JDK17
SpringBoot2.7.183.x最新版2.7.x是保守又稳定的最终版本,社区文档和教程最多
MyBatis Startermybatis-spring-boot-starter 2.3.2mybatis-plus-boot-starter版本必须和SpringBoot匹配,3.x系列有单独的starter
MySQL5.7 或 8.08.4 LTS8.0用得很广泛,驱动坐标要注意变化
MySQL驱动随SpringBoot管理手动指定SpringBoot2.x用mysql-connector-java,3.x用mysql-connector-j
IDEA任意版本社区版免费社区版完全够用,跑SpringBoot没有任何障碍
Maven3.8+IDEA自带Maven建议自己装一个并配好阿里云镜像

这里最核心的一句话:版本不要盲目追新。SpringBoot 3.x对JDK版本有硬性要求,JDK8是跑不起来的;同时MyBatis-Plus如果想配SpringBoot3,也得用mybatis-plus-spring-boot3-starter,很多老教程根本不会提这件事。如果你是为了快速交作业或者快速验证技术方案,用2.7.18加JDK8这一套组合,基本不会出现“教程里明明可以我这边就是不行”的悲催情况。

1.3 环境检查:动手前的一次体检

在IDEA里新建工程之前,先在命令行里确认一下三件套有没有就绪。这一步花三分钟,能避免后面半小时的尴尬。

java -version mvn -version mysql --version
  • 如果java -version显示的是1.8.x,说明当前默认JDK是8,搭配SpringBoot 2.7.x正合适;如果显示17.x或者21.x,建议直接把SpringBoot版本选到3.x。
  • 如果你从来没有主动装过Maven,命令行会提示找不到命令。这时候也不用慌,IDEA自带的Maven也能用,但内置源是国外的,首次下载依赖会慢到怀疑人生。建议去Maven官网下载一个3.8以上的版本,解压后配置一下settings.xml里的阿里云镜像。
  • MySQL检查之后,还需要确认服务本身在跑。Windows下可以打开服务管理器看“MySQL80”这个服务是不是“正在运行”,macOS下可以执行brew services list看状态,或者直接尝试用命令行客户端连一下。

这一套检查下来,环境层面基本稳了,后面遇到的错误就集中在工程内部的配置上。

2. IDEA创建SpringBoot工程与核心配置

2.1 从IDEA新建项目的完整过程

打开IDEA之后,选择File → New → Project,左侧选Spring Initializr。这里有一个很容易踩的点:如果你在IDEA里选择直接从官网start.spring.io拉取项目模板,在国内网络环境下经常会卡在“Loading template”这一步,等很久都没反应。解决办法是把Server URL换成阿里云的镜像地址:https://start.aliyun.com。

选好镜像之后,填写Group(通常是反向域名,比如com.example)、Artifact(工程名),然后选Java版本。如果你打算用SpringBoot 3.x就选17,用SpringBoot 2.x就选8。接下来是依赖选择界面,我实际操作下来更建议一个依赖都别勾,直接生成一个空壳工程,然后自己往pom.xml里手动加依赖。原因有两个:一是IDEA在线模板生成的依赖版本不一定是你本地Maven仓库里缓存好的版本,首次导入容易触发大量下载;二是手动加依赖的过程能让你清楚知道每行坐标是干什么用的,后面调错的时候不会一头雾水。

工程生成后会自动帮你建好标准的Maven目录结构:src/main/java放Java源码,src/main/resources放配置文件,pom.xml是Maven的依赖总控。如果你喜欢整洁,还可以删掉src/test目录下的临时测试类,不过保留也无所谓。

2.2 依赖pom.xml的写法与版本陷阱

这一步是重点,直接看修正后的pom.xml核心部分。

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- Web场景启动器:内置Tomcat、SpringMVC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- MyBatis场景启动器 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.3.2</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <!-- Lombok:简化getter/setter,看个人习惯可加可不加 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>

这里有几个版本陷阱需要重点提醒。

第一,mybatis-spring-boot-starter没有跟随SpringBoot的依赖管理,也就是说它不在spring-boot-dependencies的BOM里,因此必须显式写version。漏写版本号的时候,Maven不会报错,但会提示“unknown artifact”,很多新手在这一步就卡住了。

第二,MySQL的驱动坐标在SpringBoot 2.7.x和3.x里不一样。2.x版本里默认管理的是mysql:mysql-connector-java,3.x版本已经改成了com.mysql:mysql-connector-j。如果你从3.x的教程复制坐标到2.x工程里,有可能出现类找不到的情况。

第三,JDK8真的不要再硬配SpringBoot 3.x了,启动会直接报不支持class file version的错,因为JDK8不支持编译出来的Java17字节码。

2.3 application.yml连接数据库的配置姿势

新建好工程后,在src/main/resources目录下新建application.yml,这是SpringBoot的全局配置文件。配置数据库连接和MyBatis的写法我直接给出来:

server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/demo_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: 你的密码 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.demo.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl logging: level: com.example.demo.mapper: debug

逐个解释一下关键参数。

driver-class-name一定要写com.mysql.cj.jdbc.Driver,这是MySQL 8以后的驱动类名。网上有些老教程写的是com.mysql.jdbc.Driver,在新驱动版本里这个类已经没有了,连不上时第一个怀疑这个。

URL里那一长串参数,每一个都是踩坑换来的:

  • useUnicode=true和characterEncoding=utf8解决中文乱码问题。现在更推荐直接用utf8mb4,但URL中写utf8即可,数据库端用utf8mb4也能匹配。
  • useSSL=false是高频需求。MySQL8默认会尝试SSL连接,本地开发环境没有SSL证书,不关闭会报SSL连接错误。
  • serverTimezone=Asia/Shanghai解决时区问题。不写的话MySQL8也会报"The server time zone value"之类的异常。
  • allowPublicKeyRetrieval=true和MySQL8的默认认证插件caching_sha2_password有关,不配置会报Public Key Retrieval is not allowed。
  • mybatis.mapper-locations告诉MyBatis去哪里找XML映射文件。我习惯把所有Mapper的XML放到resources/mapper目录下。
  • type-aliases-package配成实体类所在包,这样XML里的resultType可以直接写实体类名,不用写全限定名。
  • map-underscore-to-camel-case是一键解决数据库下划线字段映射到Java驼峰属性的钥匙,后面查数据的时候你会感谢这个配置。

如果编译或运行时碰到“Failed to configure a DataSource”这类报错,十有八九是yml没被扫描到,或者yml里的缩进写错了。YAML对缩进极其敏感,换行后统一用空格,不要用Tab。

3. MyBatis集成:从接口到SQL的完整链路

3.1 启动类、Mapper扫描与Mapper接口

配置文件和依赖到位后,我们需要让Spring容器知道Mapper接口在哪里。最省事的做法是在启动类上添加@MapperScan注解。

package com.example.demo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.example.demo.mapper") public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }

@MapperScan的作用是扫描指定包下的所有接口,凡是标记了@Mapper注解的接口都会被注册成Spring Bean。使用@MapperScan之后,每个Mapper接口上就不需要再重复加@Mapper了,这也是我比较推荐的方式——扫描一次,全包生效。

然后创建实体类和Mapper接口。这一步的关键是让实体类属性名与Mapper接口方法名形成对应关系。

package com.example.demo.entity; public class User { private Long id; private String name; private Integer age; 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; } }
package com.example.demo.mapper; import com.example.demo.entity.User; import java.util.List; @Mapper public interface UserMapper { List<User> findAll(); User findById(Long id); int insert(User user); int update(User user); int deleteById(Long id); }

注意Mapper接口里的方法名必须和XML里的语句id完全一致,这是MyBatis接口绑定的前提。

3.2 XML映射文件:namespace和动态SQL

在resources/mapper目录下创建UserMapper.xml,文件名随意,但要和mapper-locations匹配。文件内容核心是namespace和SQL语句。

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.demo.mapper.UserMapper"> <select id="findAll" resultType="User"> SELECT id, name, age FROM user ORDER BY id DESC </select> <select id="findById" resultType="User"> SELECT id, name, age FROM user WHERE id = #{id} </select> <insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user (name, age) VALUES (#{name}, #{age}) </insert> <update id="update" parameterType="User"> UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id} </update> <delete id="deleteById"> DELETE FROM user WHERE id = #{id} </delete> </mapper>

namespace必须写Mapper接口的全限定名,一字不差,否则MyBatis在启动时就会报Invalid bound statement错误。

resultType这里写了User,是因为前面在yml里配置了type-aliases-package,MyBatis会自动把包下所有类的简单类名注册为别名。如果没有配type-aliases-package,就要写完整的com.example.demo.entity.User。

动态SQL是MyBatis最常用的功能,特别是“某个条件是空就不拼进WHERE”这种场景。这里直接给一个带if和where标签的查询写法,顺便解决“条件不生效”的常见困惑:

<select id="findByCondition" resultType="User"> SELECT id, name, age FROM user <where> <if test="name != null and name != ''"> AND name LIKE CONCAT('%', #{name}, '%') </if> <if test="age != null"> AND age = #{age} </if> </where> </select>

为什么用where标签而不是手写where 1=1?因为where标签会在第一个条件成立时自动拼上WHERE关键字,同时智能去掉多余的AND前缀,这样SQL更干净,也不会因为少写一个AND导致语法错误。条件不生效常见的原因有三个:test表达式里写了不存在的方法参数;用户传入的参数本身就是null或空字符串;字段比较时数据类型不匹配。排查看这三处基本能解决九成问题。

3.3 SQL日志打印与调试技巧

调试MyBatis时,最痛苦的事情是“不知道SQL执行了什么、传了什么参数”。两种方式可以把SQL打印出来。

第一种,yml里已经写好的log-impl: org.apache.ibatis.logging.stdout.StdOutImpl,这种方式会直接在控制台输出MyBatis的内部日志,通俗讲就是简单粗暴,适合本地开发调试。

第二种,通过logging.level控制指定包下的日志级别为debug,输出会更规范,也会融合SpringBoot的日志体系。

实际控制台打印出来的效果大概是这样的:

==> Preparing: SELECT id, name, age FROM user WHERE id = ? ==> Parameters: 1(Long) <== Total: 1

解读这三个关键标记:Preparing行是预编译后的SQL模板,问号表示占位符;Parameters行是实际传入的参数值和类型;Total行是本次查询返回的结果行数。如果SQL执行了但返回0行,问题多半在数据本身;如果SQL根本没打印出来,那问题多半在Mapper接口和XML的绑定关系上。

另外,精力和时间比较充裕的话,可以深入看一下MyBatis的初始化过程。简单说就是SqlSessionFactoryBuilder读取配置文件,构建Configuration对象,再把每个Mapper的Statement注册进这个对象,后续每次调用都是从这个配置中心取SQL。理解了这一层,很多“找不到XXX”的报错一眼就能定位成因。

3.4 缓存和类型处理的一些补充

MyBatis默认自带一级缓存和二级缓存。一级缓存是SqlSession级别,同一个会话里重复查询相同SQL可以直接走缓存,不用打数据库;二级缓存是namespace级别,需要手动开启,实体类还必须实现Serializable接口,否则使用时会报序列化异常。面试题里经常问“MyBatis二级缓存为什么不推荐用于多表操作”,原因就是二级缓存基于namespace,多表查询时一旦数据更新而缓存没同步,很容易出现脏读。项目实战中我会在单表且读多写少的地方才考虑开二级缓存,大部分场景保持默认更稳妥。

关于TypeHandler,它在MyBatis里负责Java类型和JDBC类型的双向转换。比如Java的LocalDateTime和MySQL的datetime之间,MyBatis内置的LocalDateTimeTypeHandler已经能搞定;如果遇到自定义类型,可以实现TypeHandler接口,在setParameter和getResult里写转换逻辑,然后在XML里用typeHandler属性指定。这类高级内容不用急着全学,先把CRUD跑通,再考虑扩展。

4. MySQL数据库准备与连接细节

4.1 建库建表与测试数据

服务端的工程搭好了,现在把数据库这一侧准备好。我习惯先建一个独立的数据库,而不是所有表都堆到root账号下,这样后续维护也清晰。

CREATE DATABASE IF NOT EXISTS demo_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE demo_db; CREATE TABLE user ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, age INT DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO user (name, age) VALUES ('张三', 18), ('李四', 20), ('王五', 25);

数据库字符集我统一用utf8mb4,原因很简单:MySQL里的utf8其实只是utf8mb3,最多只能存3字节的字符,遇到emoji或者一些生僻字就会报“Incorrect string value”错误。utf8mb4才是完整的UTF-8实现,能兼容所有字符,这也是当前社区的主流约定。

如果你刚装了MySQL还没有任何数据,上面的SQL一步到位。如果你已经有一个现成的数据库,注意看表结构里的字段名,比如user_name这种下划线格式,Java实体里对应的是userName,两者之间的桥梁就是前面yml里配置的map-underscore-to-camel-case。

4.2 连接URL参数逐个拆解

连接串是很多人反复出错的地方。我见过有人把URL写成jdbc:mysql://localhost:3306/demo_db就完事了,本地能跑通说明运气好,但凡MySQL版本稍微新一点就会暴露问题。把最常用的一套参数再展开讲一下。

参数作用不配置的后果
useUnicode=true启用Unicode字符集支持中文可能乱码
characterEncoding=utf8指定字符编码中文乱码
useSSL=false关闭SSL连接MySQL8可能报SSL连接错误或大量告警
serverTimezone=Asia/Shanghai指定时区报The server time zone value错误
allowPublicKeyRetrieval=true允许客户端获取RSA公钥报Public Key Retrieval is not allowed

凡是MySQL8的用户,建议把这几个参数全部配上,省心。MySQL5.7用户至少也要配置useUnicode和serverTimezone,否则迟早会碰到乱码。

如果你的MySQL装在其他机器上,比如服务器或者同事电脑,URL里的localhost要换成对应IP,同时注意对方防火墙有没有放行3306端口。连接报超时的时候优先检查这个,而不是反复怀疑账号密码。

4.3 MySQL8认证插件与旧工程兼容问题

MySQL8开始默认的认证插件是caching_sha2_password,这意味着即使密码正确,老版本驱动连接时也可能失败。最常见的报错就是“Public Key Retrieval is not allowed”。解决方案有两个:

方案一:在连接URL上加allowPublicKeyRetrieval=true,前面yml里已经写好了。 方案二:把MySQL用户的认证插件改回mysql_native_password。

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码'; FLUSH PRIVILEGES;

从长远角度看,方案一更符合版本趋势,方案二适合快速兼容旧代码。另外要提醒一句,生产环境不建议真的用root账号直接连数据库,最小权限原则还是要遵守。建一个专门的账号,只授权需要的库,这样万一出问题可以快速回收权限。

4.4 MySQL安装与客户端工具要点

如果你还没装MySQL,这里说几个安装要点。Windows下用MySQL Installer安装时注意选Server only就够了,端口保持默认3306,认证方式如果本地开发就直接选“Use Legacy Password Authentication”能避免很多兼容性问题,字符集选utf8mb4,服务设置成自动启动。macOS下用Homebrew安装最方便,装完记得执行brew services start mysql把服务拉起来。

客户端工具这块,Navicat确实是很多人的首选,图形化界面清晰,功能也全。但需要特别说明的是不要花心思去折腾破解版,现在很多版本的破解都伴随安全隐患,完全没必要。正版有试用期,也可以用开源的DBeaver、DataGrip的社区版,功能完全够用,还省心。连接不上MySQL时先看三件事:MySQL服务有没有在跑、3306端口有没有被占用、用户名密码是否匹配,这个顺序排查一遍基本能解决大部分问题。

5. 完整CRUD实操验证

5.1 Service和Controller的代码实现

数据访问层就绪后,补上Service和Controller两层,一个完整的接口链路就通了。

package com.example.demo.service; import com.example.demo.entity.User; import com.example.demo.mapper.UserMapper; import org.springframework.stereotype.Service; import java.util.List; @Service public class UserService { private final UserMapper userMapper; public UserService(UserMapper userMapper) { this.userMapper = userMapper; } public List<User> findAll() { return userMapper.findAll(); } public User findById(Long id) { return userMapper.findById(id); } public int insert(User user) { return userMapper.insert(user); } public int update(User user) { return userMapper.update(user); } public int deleteById(Long id) { return userMapper.deleteById(id); } }
package com.example.demo.controller; import com.example.demo.entity.User; import com.example.demo.service.UserService; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/users") public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping public List<User> list() { return userService.findAll(); } @GetMapping("/{id}") public User detail(@PathVariable Long id) { return userService.findById(id); } @PostMapping public int create(@RequestBody User user) { return userService.insert(user); } @PutMapping public int update(@RequestBody User user) { return userService.update(user); } @DeleteMapping("/{id}") public int delete(@PathVariable Long id) { return userService.deleteById(id); } }

这个Controller用了RESTful风格,接口路径是/api/users。你可能会注意到,Controller里我没有写任何业务判断,这是故意的——演示工程重点是把链路跑通,真实项目中你会在Service层加参数校验、异常处理、权限控制等逻辑。另外很多人会问,为什么Controller的字段注入不直接用@Autowired?我这里用的是构造器注入,Spring官方也推荐这种方式,好处是依赖关系明确,方便测试时手动传参。

5.2 从浏览器到数据库的全链路测试

代码写完就可以启动验证了。运行DemoApplication的主方法,看到控制台出现“Tomcat started on port 8080”说明启动成功。然后在浏览器里访问:

http://localhost:8080/api/users

如果一切正常,你会看到类似这样的JSON数组:

[ {"id":1,"name":"张三","age":18}, {"id":2,"name":"李四","age":20}, {"id":3,"name":"王五","age":25} ]

这说明从Controller到Service到Mapper到XML再到MySQL的整条链路已经通了一条。下一步测试新增,用Postman或者IDEA自带的HTTP Client发送一个POST请求:

POST http://localhost:8080/api/users Content-Type: application/json { "name": "赵六", "age": 28 }

返回1表示插入成功。接着再访问GET /api/users,你会发现列表里多了一条赵六的记录。这个流程走通之后,工程骨架就彻底立起来了。

如果返回的不是JSON而是404页面,大概率是路由路径写错了,检查一下@RequestMapping和@GetMapping注解的路径拼接。如果返回500,看控制台的异常堆栈,最常见的是SQL语法问题或者参数绑定问题。

5.3 再进一步:这个工程还能怎么扩展

骨架搭好之后,后面可以扩展的方向就非常多了,我简单提几个经常被问到的:

  • 引入MyBatis-Plus替代原生MyBatis,实体类继承BaseMapper后自带增删改查方法,可以少写很多XML。
  • 加PageHelper做分页,或者用MyBatis-Plus自带的分页插件,数据库排序需求也顺手解决。
  • 把SpringBoot的Banner换成自定义文字,纯娱乐项目可以用在线Banner生成工具生成一份ASCII艺术字放到banner.txt里。
  • 前端项目如果用的Vue,打包后的dist目录可以直接扔进src/main/resources/static,SpringBoot会自动托管静态资源,实现前后端一体部署。
  • 接Redis做缓存、接ActiveMQ做消息队列、接MinIO做对象存储,都是在这个工程基础上加依赖、加配置的事,核心骨架不需要改动。

6. 常见问题与排查技巧实录

6.1 高频问题速查表

这里把我见过的高频问题整理成了速查表,建议收藏。

现象可能原因解决方案
启动报Failed to configure a DataSource没有配置数据源或yml未被加载检查application.yml是否在resources目录下,数据源配置是否完整
Access denied for user用户名或者密码错误用命令行客户端试连,确认账号密码
Public Key Retrieval is not allowedMySQL8认证插件问题URL添加allowPublicKeyRetrieval=true
Unknown database 'demo_db'数据库不存在执行CREATE DATABASE语句
Invalid bound statement (not found)namespace或id跟接口不一致检查XML的namespace和mapper-locations路径
java.sql.SQLException: No suitable driver驱动坐标或driver-class-name错误确认用的是com.mysql.cj.jdbc.Driver
Tomcat started on port 8080失败端口被占用改server.port或kill占用进程
查询结果全是null字段映射失败开启map-underscore-to-camel-case或用resultMap
中文存入数据库变成乱码数据库或连接字符集不对统一使用utf8mb4,URL加characterEncoding
编译报程序包lombok不存在IDEA未开启注解处理安装Lombok插件并开启Enable annotation processing

6.2 排查思路:学会看日志的“Caused by”

遇到报错,第一反应不要慌,更不要盯着红字看半天。日志信息通常是一条调用链,真正的根源在最后的Caused by那一行。比如一大串异常里往往有个“Caused by: java.sql.SQLException”,后面跟的内容才是真正需要解决的问题,前面的都是表象。

养成一个好习惯:报错之后先复制Caused by那一行去搜索引擎查,不要复制完整的巨大堆栈。搜到的解决方案往往是针对根因的,更精确也更快。我见过不少人把整个日志贴到群里问其他人,其实自己稍微静下心看两分钟就能找到答案。

另一个常见问题是工程里有两套配置环境,比如application.yml和application.properties同时存在,SpringBoot默认两者同时加载时有优先级规则,yml的优先级略高。如果不确定当前生效的是哪份配置,可以启用SpringBoot的启动日志,启动时会打印Active Profiles,确认一下没有加载到奇怪的配置。

6.3 我踩过几次坑之后总结的几条经验

最后分享几条我自己的实操经验,有的是一次次熬夜排查换来的。

第一条,不要去追求最新版本。具体到SpringBoot,2.7.18和3.x我都在用,如果是为了学框架、交作业、做演示,2.7.x的教程多、问题少、资料全。等工作几年真正吃透原理后,再迁移到3.x也不迟。

第二条,首次引入依赖时尽量一次性把Maven镜像配好。去Maven官网下载的默认配置连接的是中央仓库,国内下载依赖慢得让人崩溃。在Maven的settings.xml里加上阿里云镜像:

<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>

配置完记得重启IDEA,让新设置生效。

第三条,IDEA本身不用折腾什么花活。社区版免费那款已经能创建SpringBoot工程、写代码、跑调试,完全够用。家里电脑和企业电脑我都用社区版跑过这个工程,一点问题没有。遇到“配置不生效”“插件装不上”之类的问题,先想想是不是版本差异,再看官方文档,别一开始就怀疑开发工具本身。

第四条,数据库密码这类敏感信息不要硬编码在yml里。本地演示可以图方便,但项目一旦要部署到服务器,建议用环境变量替换:

password: ${DB_PASSWORD}

这样配置文件可以提交到Git仓库,真正的密码只存在部署环境里,安全风险小很多。

最后一条,也是我最想说的一条:第一次跑通时,把整个流程在心里过一遍,从浏览器地址栏到数据库表结构,每一步数据是怎么流的。这个思维习惯一旦养成,后面你排查任何Web项目的问题都会快很多。我自己后来能快速定位各种生产环境问题,靠的不是奇技淫巧,就是这套对调用链的熟悉感。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 3:23:48

搭建AI编程基础设施:统一管理多模型API与上下文的实践

最近把小半年散的 AI 编程工具整理成了一套统一的基础设施。以前写代码的时候&#xff0c;这边开一个 ChatGPT&#xff0c;那边开一个 Claude&#xff0c;本地终端还挂着 DeepSeek 的 API&#xff0c;遇到要切换供应商就得重新配环境变量&#xff0c;配完 key 又得重启终端&…

作者头像 李华
网站建设 2026/10/3 3:23:29

基于SpringBoot+Vue的电影评论网站管理系统完整开发实战

最近一段时间&#xff0c;前后有好几个准备做毕业设计或者课程设计的同学来找我看代码&#xff0c;问的最多的就是同一个方向&#xff1a;基于SpringBootVue的电影评论网站管理系统。这东西乍一听感觉很常规&#xff0c;但真正从头到尾完整做一遍&#xff0c;你会发现它把Java后…

作者头像 李华
网站建设 2026/10/3 3:23:27

基于Scala与MySQL的交通拥堵预测:数据库课设高分实战解析

简介&#xff1a;这是一份基于Scala的交通拥堵预测源码&#xff0c;源自大三学期的高分数据库课程设计&#xff0c;适合计算机科学与技术、大数据、人工智能等专业学生用作课程设计、期末大作业或项目实战演练。压缩包共50个文件&#xff0c;以18个Scala源文件为核心&#xff0…

作者头像 李华
网站建设 2026/10/3 3:22:38

Spark+Hive交通智能研判系统源码解析与实战避坑指南

简介&#xff1a;《基于SparkHive的交通智能研判系统》是一套用于毕业设计和课程设计场景的大数据实践项目&#xff0c;基于Spark与Hive两大组件构建&#xff0c;面向交通流量实时计算、历史数据管理和智能研判分析等问题&#xff0c;适合正在学习或开发分布式数据处理系统的读…

作者头像 李华
网站建设 2026/10/3 3:22:20

AI Agent多智能体仿真:用大模型构建舆论推演沙盘

开门见山说一个事&#xff1a;把“社会舆论”放进AI里做思想实验&#xff0c;现在已经不是科幻设定&#xff0c;而是一套可以落地的工程方案。核心思路很简单——用大模型驱动一批虚拟智能体&#xff0c;让它们拥有不同身份、立场、信息渠道和表达习惯&#xff0c;在一个人造的…

作者头像 李华
网站建设 2026/10/3 3:22:02

S7-1200 PID_Temp恒温控制实战:从组态到参数整定

1. 做恒温控制&#xff0c;为什么我盯上了PID_Temp这条工艺指令做工业自动化这些年&#xff0c;温控项目几乎每个工程师都会碰到。从塑料挤出机的料筒加热&#xff0c;到烘箱、干燥罐、反应釜&#xff0c;再到实验室的小型恒温槽&#xff0c;场景千千万万&#xff0c;但控制逻辑…

作者头像 李华