SpringBoot+MyBatis+MySQL项目搭建:IDEA避坑指南
发布时间:2026/10/18 17:15:52 锦皓数字建站

如果你准备在IDEA里把一个SpringBootMyBatisMySQL的工程从零搭起来这篇帖子应该能帮你少走不少弯路。这几乎是我见过最经典的后端起步组合之一SpringBoot负责降低配置成本MyBatis负责灵活的SQL掌控力MySQL作为最常用的数据存储三者搭配基本覆盖了日常后端CRUD开发的主流场景。无论你是刚学完Java语法准备做第一个完整项目的学生还是工作中需要从零搭建一套服务接口的开发者这篇文章都会从环境、建项目、连库、写Mapper、调接口一直讲到常见的坑和优化习惯全程可复现。我这里不会只丢一堆官方文档链接而是把我自己在本地反复拆过、重建过多次的步骤和心得直接摆出来。版本怎么选、依赖怎么加、配置怎么写、报错怎么解每一步都会说明为什么这样做以及我在实操中踩过哪些雷。你照着走一遍应该能在半小时左右把一套能跑通的工程立起来。1. 环境准备与版本组合少走弯路的选型1.1 版本搭配为什么这么重要很多人搭项目第一步就直接new project结果依赖一拉启动一跑要么JDK版本不兼容要么SpringBoot和MyBatis版本对不上要么MySQL驱动报错。这些大多不是技术难题而是版本组合没选对。我的建议是如果你不是有特殊需求最好用一套经过大量实践验证的温和配置。本地开发和学习我推荐下面这组JDK1.8Java 8或 17。如果你是刚入门直接用Java 8最稳很多老项目也仍是Java 8资源和踩坑经验最多。SpringBoot2.x系列。我常用的是2.7.x这个版本兼容性很好既支持Java 8也兼容Java 17。MyBatis使用mybatis-spring-boot-starter版本2.3.x或与SpringBoot 2.7配套的版本。MySQL5.7或8.0都可以。8.0需要注意驱动类名和时区配置5.7相对省心。Maven3.6.3以上即可IDEA自带或独立安装都行。这里要特别说明一点SpringBoot 3.x已经发布但它默认基于Jakarta EE要求JDK 17以上并且部分第三方starter的适配还不算特别成熟。如果只是为了快速搭建一个稳定的工程我不会首选3.x。等你在2.x上把整体流程跑熟了再考虑升级也不迟。1.2 安装与基础检查IDE方面直接用IDEA的社区版或专业版都可以。社区版虽然缺少部分Spring Initializr的默认集成但完全可以通过手动创建项目或者在线初始化来解决后面的步骤我会讲到。安装时没有什么特殊注意点只需要确保IDEA安装目录下的JDK与你项目的JDK版本匹配。MySQL安装时我建议额外注意三点字符集选择utf8mb4而不是utf8。utf8mb4是utf8的超集能存emoji和一些生僻字避免后续数据写入报错。root密码设置为一个你记得住但不简单的值后续配置数据源要用。记住端口号默认3306如果被占用可以改只是后面连接配置也要同步修改。验证环境是否就绪很简单在终端输入java -version、mvn -v、进入MySQL命令行执行select version();能正常输出版本号就说明基础环境没问题。2. SpringBoot项目初始化实操2.1 在IDEA中创建项目的两种方式IDEA里创建SpringBoot项目我常用两种路径。第一种使用IDEA的Spring Initializr向导。新建项目时选择Spring Initializr然后选择SDKJDK版本填写Group和Artifact接着勾选依赖。这种方式生成的工程结构规范自带pom.xml和启动类。但如果你用的社区版或者网络原因访问不了初始化服务可以改用第二种。第二种直接新建一个Maven项目然后在pom.xml里自己加入SpringBoot父依赖和需要的starter。这种方式更灵活也能让你更清楚每个依赖的作用。我自己早期学习时反而更喜欢第二种因为每加一个依赖都能看到对应的功能和版本变化。不管哪种方式最终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-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency /dependencies如果使用SpringBoot 2.7.xmysql-connector-java的版本会由父依赖统一管理不需要手动指定。SpringBoot 3.x则要用com.mysql:mysql-connector-j这个新坐标这也是我为什么不推荐新手上3.x的原因之一。2.2 认识生成的目录结构项目创建完成后默认目录大概是这样的src/main/java/com/example/demo ├── DemoApplication.java // 启动类 └── ... src/main/resources ├── application.properties或application.yml ├── static └── templates src/test/java启动类上标注了SpringBootApplication它同时包含了SpringBootConfiguration、EnableAutoConfiguration和ComponentScan三个注解的功能。右键运行main方法如果控制台出现Spring Boot的启动日志说明骨架已经活了。我习惯把配置文件改成application.yml因为它通过缩进表达层级比properties更清晰尤其是后面要配数据源、MyBatis、日志的时候层级关系一眼就能看懂。3. MyBatis集成从配置到跑通3.1 数据源配置前的准备在写数据源配置之前先确认MySQL里已经建好了数据库和表。你可以在MySQL命令行执行CREATE DATABASE demo_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后创建一张简单的用户表CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键, name varchar(50) NOT NULL COMMENT 姓名, age int(11) DEFAULT NULL COMMENT 年龄, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;注意user是MySQL的保留字最好加上反引号或者换一个表名比如t_user。我在实际项目中就遇到过因为表名和关键字冲突导致SQL执行失败的情况这点要重视。3.2 application.yml里到底要写什么现在把数据源信息填进application.ymlserver: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/demo_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue 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里面每一项都值得展开说。driver-class-name如果是MySQL 8.0以上用com.mysql.cj.jdbc.Driver如果是MySQL 5.7或早期版本可以用com.mysql.jdbc.Driver但我直接统一用cj驱动也没问题。url里的几个参数要解释一下。useUnicodetruecharacterEncodingutf8是保证写入数据不乱码。serverTimezoneAsia/Shanghai很重要因为MySQL 8.0默认时区是UTCJava连接时如果不指定通常会出现The server time zone value ... is unrecognized的报错。useSSLfalse是关闭SSL连接本地开发没必要开启否则容易报证书相关警告。allowPublicKeyRetrievaltrue这是MySQL 8.0在使用caching_sha2_password认证方式时需要的参数。mybatis.mapper-locations指定了XML文件的位置。我习惯把Mapper接口和XML分开XML统一放在resources/mapper下接口放在Java的mapper包下。如果你选择纯注解方式这个配置可以不写。map-underscore-to-camel-case: true是开启数据库下划线字段到Java驼峰属性的自动映射。比如数据库字段create_time会自动映射到实体属性createTime不用手动写resultMap。这个开关我基本是必开的。3.3 手写Mapper两种风格对比MyBatis的Mapper接口既可以用注解也可以用XML文件。我在实际开发中更推荐XML方式因为SQL复杂起来之后XML的可读性、可维护性都更好而且支持动态SQL会更灵活。但注解方式更适合简单的单表操作。先看注解方式接口里直接写SQLMapper public interface UserMapper { Select(SELECT * FROM t_user WHERE id #{id}) User findById(Long id); Insert(INSERT INTO t_user(name, age) VALUES(#{name}, #{age})) int insert(User user); }再看XML方式。接口只声明方法Mapper public interface UserMapper { User findById(Long id); int insert(User user); }然后在resources/mapper/UserMapper.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 idfindById resultTypecom.example.demo.entity.User SELECT * FROM t_user WHERE id #{id} /select insert idinsert useGeneratedKeystrue keyPropertyid INSERT INTO t_user(name, age) VALUES(#{name}, #{age}) /insert /mapper注意namespace必须写成Mapper接口的全限定名id必须和接口方法名一样不然会报Invalid bound statement (not found)。这个错我打赌每个人都遇过。4. 完成一条完整链路建表→实体→Mapper→Service→Controller4.1 实体类怎么写才规范数据表和Mapper接口都有了接下来写实体类。我建议实体类属性和表字段一一对应使用包装类型而不是基本类型比如Integer而不是int。因为数据库中的NULL值无法映射到基本类型包装类可以保持null避免反序列化和业务判断时出现问题。public class User { private Long id; private String name; private Integer age; private Date createTime; // getter/setter }很多人喜欢用Lombok来简化getter/setter这个当然可以。不过我建议新手在初学阶段先手写一两遍等彻底理解了再引入Lombok否则出了问题可能连IDE的编译错误都看不懂。4.2 Service层和Controller层的实现Service层负责业务逻辑Controller层负责接收HTTP请求和返回响应。不要把所有逻辑直接堆在Controller里这是工程化最基本的底线。先写Service接口和实现类public interface UserService { User getUserById(Long id); int addUser(User user); }Service public class UserServiceImpl implements UserService { Resource private UserMapper userMapper; Override public User getUserById(Long id) { return userMapper.findById(id); } Override public int addUser(User user) { return userMapper.insert(user); } }这里注意注入方式。我习惯用Resource注入Mapper它按名称优先不会出现同类型多个实现时的歧义。如果你用Autowired绝大多数情况也没问题但在某些复杂场景下会报No qualifying bean of type所以直接养成Resource的习惯能少点事。Controller层再简单包装一下RestController RequestMapping(/user) public class UserController { Resource private UserService userService; GetMapping(/{id}) public User getUser(PathVariable Long id) { return userService.getUserById(id); } PostMapping public int addUser(RequestBody User user) { return userService.addUser(user); } }这里没有使用RestControllerAdvice做统一返回体包装也没有用Result类因为核心是先把链路打通。实际项目里肯定要设计一个ResultT结构这个后面再拓展。4.3 启动运行与接口测试把工程启动后可以用浏览器直接访问http://localhost:8080/user/1。如果数据里有id1的记录页面会返回JSON格式的用户信息。如果是POST新增建议用Postman或IDEA自带的HTTP Client工具。我在测试时发现一个很常见的情况POST请求直接报415 Unsupported Media Type也就是没有设置Content-Type: application/json。用Postman时要在Headers里显式添加这个Header或者选择body为raw格式并设置JSONIDEA自带的.http文件则需要写Content-Type: application/json。另外接口可以正常查询但控制台没有打印SQL日志多半是MyBatis日志配置没生效。我在application.yml里已经配置log-impl: org.apache.ibatis.logging.stdout.StdOutImpl启动后执行SQL控制台会直接输出SQL语句和参数调试效率很高。发现SQL执行慢或结果不对时第一件事就是看这个日志。5. 常见问题排查与避坑实录5.1 启动类报错Something went wrong这类报错信息往往特别长但核心信息往往藏在栈顶或Caused by部分。我见过最多的情况是数据库没启动或连接被拒。检查MySQL服务是否运行。URL写错、密码错误。注意url里的jdbc:mysql://前面别漏掉jdbc:前缀。缺少驱动依赖。确认pom里加了mysql-connector-java。还有一个容易忽略的如果你的application.yml文件缩进不对比如冒号后面没加空格IDEA可能不会直接报语法错误但SpringBoot启动时会提示org.yaml.snakeyaml解析异常。YAML对缩进非常严格建议用spring-boot-configuration-processor配合IDEA的格式化工具来检查。5.2 数据访问层的报错Invalid bound statement这个报错几乎是MyBatis新手必遇org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.example.demo.mapper.UserMapper.findById排查思路按照这几点来Mapper接口和XML文件的namespace是否一致。XML中的id是否和接口方法名一致。mapper-locations路径是否写对比如我放在resources/mapper下配置就是classpath:mapper/*.xml。Mapper接口上是否有Mapper注解或者在启动类上加了MapperScan。我推荐在启动类或配置类上写MapperScan(com.example.demo.mapper)这样就不需要每个Mapper接口都加Mapper一劳永逸。这里再补充一个容易掉的坑如果使用了MapperScan同时又给某个Mapper加了Mapper其实是没问题的但会给人造成困惑最好统一一种方式。5.3 数据库连接参数的坑我单独把MySQL 8.0的时区坑拿出来说因为它的报错信息极具迷惑性The server time zone value Öйú±ê׼ʱ¼ä is unrecognized or represents more than one time zone.解决办法就是在连接URL后面加上serverTimezoneAsia/Shanghai。如果还有乱码显示可能是IDEA控制台编码问题在IDEA的Help - Edit Custom VM Options里加一行-Dfile.encodingUTF-8然后重启IDEA。另外连接MySQL 8.0时还会遇到Public Key Retrieval is not allowed解决办法是URL参数加allowPublicKeyRetrievaltrue。这个参数的含义是允许客户端向服务器请求公钥用于caching_sha2_password认证。本地开发当然可以直接开启生产环境如果安全要求高建议换用mysql_native_password或做其他证书配置这里不展开。6. 项目优化与工程化习惯6.1 分层结构与命名规范一个能跑通的Demo只是开始。如果你想在团队里提交代码或者让项目真正可维护至少要做到以下几点包名按com.xxx.项目名.controller/service/mapper/entity/vo/dto分层。不要让所有类都挤在一起。Controller只做参数接收和响应包装不写业务SQL。Service接口和实现分离接口定义能力实现类写逻辑。好处是以后替换实现、做AOP拦截、写单元测试都方便。Mapper只负责数据访问不要在里面写业务判断。实体类Entity对应表结构DTO/VO对应接口入参出参不要共用一套类传到底。我见过很多项目为了省事直接把Entity当作返回对象丢给前端结果暴露了数据库字段甚至把密码、手机号全吐出去了。这种习惯一定要从一开始就避免。6.2 事务、连接池与日志配置MyBatis集成SpringBoot后事务控制非常简单。在Service实现类的公共方法上加上Transactional注解即可。比如新增用户时需要同时插入用户表和日志表如果第二张表插入失败第一张表不应该有残留数据这时候事务就发挥作用了。Transactional(rollbackFor Exception.class) public int addUser(User user) { userMapper.insert(user); logMapper.insertLog(add user: user.getName()); return 1; }注意rollbackFor Exception.class最好显式声明。因为Spring默认只在RuntimeException和Error时回滚而受检查异常不会触发回滚。很多新手写了Transactional后捕获了异常却发现了脏数据就是栽在这里。连接池方面SpringBoot 2.x默认用的HikariCP性能非常优秀基本不用额外调参。如果你需要调整最大连接数可以在application.yml里加spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5这些参数要看数据库实例规格和业务并发量来定初学者保持默认即可。日志配置也值得重视。虽然MyBatis标准输出能打印SQL但在生产环境会刷屏。更强的做法是引入logback-spring.xml把业务日志、错误日志、SQL日志分开。比如logging: level: com.example.demo.mapper: debug这只在开发环境输出SQL生产环境设为info。这样既方便排查又不影响性能。6.3 后续可以怎么扩展一套基础的CRUD工程搭好之后下一步我建议按顺序扩展这些能力引入MyBatis的代码生成器根据表结构自动生成实体、Mapper和XML减少重复劳动。使用PageHelper或MyBatis Plus分页插件而不是自己手写limit。设计统一返回体ResultT和全局异常处理器RestControllerAdvice。加Swagger/OpenAPI文档让接口文档自动生成。使用Spring Security或JWT做认证授权。这里面最推荐先做的是统一返回体和全局异常处理。因为现在你的接口返回的是裸数据一旦某个接口出错返回的JSON就很随意。定义一个通用的ResultT类包含状态码、消息和数据所有接口统一返回前端解析也方便。比如这样一个简单结构public class ResultT { private Integer code; private String message; private T data; // 静态方法 success、error }然后在Controller里包装一下接口响应就会规范很多。这件事我建议你今天就做因为它能立刻提升工程质量。写在最后按照上面的步骤你应该已经能在IDEA里把SpringBootMyBatisMySQL的工程完整跑通了。我个人在搭建过程中最有感触的一点是不要急着复制别人的依赖和配置而是每一行都弄明白到底有什么用。比如serverTimezoneAsia/Shanghai如果你不知道它是干什么的下次换个数据库版本或换个环境照样会报同样的时区错误而你依然不知道怎么解决。再分享一个小技巧把这次项目的初始化过程用文字或脚本记录下来。我后来新建项目几乎不再走IDEA的图形向导而是直接复制一个基础的pom.xml和application.yml模板再改个项目名就开始写代码。这样做速度更快也更不容易因为版本问题踩坑。你还可以试着把这次工程提交到代码托管平台建立一个小仓库以后每做一个新的接口就提交一次commit信息写清楚改了什么。用不了一两个月回看这些commit记录你会发现自己对项目结构的理解明显上了一个台阶。祝你在搭项目的路上一次跑通也欢迎在实际操作中遇到问题时带着报错日志来问很多问题看一眼日志就能定位到。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。