SpringBoot学习第三天|CRUD接口实战+MyBatis-Plus整合(附原理+面试高频题)
哈喽,各位一起学SpringBoot的小伙伴~ 今天是SpringBoot系列学习的第三天,承接前两天的自动装配、Bean生命周期核心原理,今天我们落地实战+深挖底层——从0到1实现一套生产级CRUD接口,整合MyBatis-Plus简化开发,同时拆解CRUD底层原理、MyBatis-Plus核心机制,同步配套面试高频题,兼顾“实战落地”和“面试备考”,新手能上手,备考能提分!
先明确本期核心目标,避免盲目学习:
-
实战:整合SpringBoot + MyBatis-Plus,实现用户模块CRUD(增删改查),支持分页、条件查询
-
原理:吃透MyBatis-Plus整合原理、CRUD接口底层执行流程、分页插件工作机制
-
面试:掌握CRUD相关面试高频题(如分页实现、逻辑删除、MyBatis和MyBatis-Plus区别),记住满分答案
-
避坑:梳理实战中常见错误(如映射异常、分页失效),给出解决方案
一、前置准备(衔接前期,快速上手)
基于第一天搭建的SpringBoot基础项目,新增3个核心依赖(pom.xml),无需手动配置MyBatis核心文件,MyBatis-Plus会自动适配,这也是它简化开发的核心优势之一。
<!-- MyBatis-Plus 核心依赖 -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version>
</dependency>
<!-- 数据库驱动(MySQL 8.0+) -->
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
<!-- lombok(简化实体类,减少get/set) -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
核心配置(application.properties),配置数据库连接和MyBatis-Plus基础参数,重点注意时区和映射路径:
# 数据库连接配置
spring.datasource.url=jdbc:mysql://localhost:3306/springboot_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# MyBatis-Plus 配置
# 映射文件路径(若有XML映射文件,需配置)
mybatis-plus.mapper-locations=classpath:mapper/**/*.xml
# 实体类别名包(简化XML中的类路径)
mybatis-plus.type-aliases-package=com.bailu12.springbootdemo.entity
# 日志打印SQL(便于调试,开发环境开启,生产环境关闭)
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
二、实战:实现CRUD接口(从实体类到接口测试)
采用“实体类 → Mapper → Service → Controller”四层架构,贴合企业开发规范,MyBatis-Plus提供的BaseMapper、IService可直接复用CRUD方法,无需手动写SQL,大幅提升开发效率。
1. 第一步:创建实体类(Entity)
对应数据库user表,使用lombok的@Data简化get/set,MyBatis-Plus注解指定表名、主键策略(面试高频考点)。
package com.bailu12.springbootdemo.entity;import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;@Data // lombok注解,自动生成get/set/toString等方法
@TableName("user") // 指定对应数据库表名(若实体类名与表名一致,可省略)
public class User {// 主键注解,指定主键策略:自增(MySQL常用)@TableId(type = IdType.AUTO)private Long id;// 普通字段,若实体类字段与表字段一致,可省略@TableFieldprivate String username;private String password;private String email;private Integer age;// 逻辑删除字段(面试高频),0=未删除,1=已删除@TableLogicprivate Integer isDeleted;// 自动填充字段(创建时间、更新时间),无需手动赋值@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;
}
2. 第二步:创建Mapper接口
继承MyBatis-Plus的BaseMapper,无需写任何方法,即可获得CRUD基础能力(底层原理后续拆解)。
package com.bailu12.springbootdemo.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bailu12.springbootdemo.entity.User;
import org.apache.ibatis.annotations.Mapper;// @Mapper:标识该接口是MyBatis的Mapper接口,SpringBoot会自动扫描并注入容器
@Mapper
public interface UserMapper extends BaseMapper<User> {// 无需手动写CRUD方法,BaseMapper已封装好
}
3. 第三步:创建Service层
Service层负责业务逻辑处理,继承IService,实现类继承ServiceImpl,复用BaseMapper的方法,同时可扩展自定义业务。
// 接口
package com.bailu12.springbootdemo.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.bailu12.springbootdemo.entity.User;public interface UserService extends IService<User> {// 可扩展自定义业务方法,如:根据年龄查询用户User selectByAge(Integer age);
}// 实现类
package com.bailu12.springbootdemo.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bailu12.springbootdemo.entity.User;
import com.bailu12.springbootdemo.mapper.UserMapper;
import com.bailu12.springbootdemo.service.UserService;
import org.springframework.stereotype.Service;@Service // 标识为Service组件,Spring自动注入
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {// 自定义业务方法:根据年龄查询用户(示例)@Overridepublic User selectByAge(Integer age) {// 用MyBatis-Plus的条件构造器QueryWrapper,无需写SQLreturn lambdaQuery().eq(User::getAge, age).one();}
}
4. 第四步:创建Controller层(CRUD接口)
编写RESTful风格接口,对应增删改查,使用@RestController返回JSON数据,接口测试用Postman即可。
package com.bailu12.springbootdemo.controller;import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bailu12.springbootdemo.entity.User;
import com.bailu12.springbootdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/user") // 接口统一前缀
public class UserController {// 注入Service@Autowiredprivate UserService userService;// 1. 新增用户(POST)@PostMappingpublic boolean addUser(@RequestBody User user) {// save()方法来自IService,自动填充createTime/updateTimereturn userService.save(user);}// 2. 删除用户(DELETE)- 逻辑删除(面试重点)@DeleteMapping("/{id}")public boolean deleteUser(@PathVariable Long id) {// removeById()默认执行逻辑删除,而非物理删除return userService.removeById(id);}// 3. 更新用户(PUT)@PutMappingpublic boolean updateUser(@RequestBody User user) {// updateById()自动填充updateTimereturn userService.updateById(user);}// 4. 单个查询(GET)@GetMapping("/{id}")public User getUserById(@PathVariable Long id) {// getById()查询未被逻辑删除的用户return userService.getById(id);}// 5. 分页查询(GET)- 面试高频@GetMapping("/page")public IPage<User> getUserPage(@RequestParam(defaultValue = "1") Integer pageNum,@RequestParam(defaultValue = "10") Integer pageSize) {// 分页构造器,pageNum=当前页,pageSize=每页条数Page<User> page = new Page<>(pageNum, pageSize);// 分页查询,返回IPage对象(包含总条数、总页数等信息)return userService.page(page);}// 6. 自定义查询(GET)@GetMapping("/age/{age}")public User getUserByAge(@PathVariable Integer age) {return userService.selectByAge(age);}
}
5. 第五步:配置分页插件(关键)
MyBatis-Plus的分页功能需手动配置插件,否则分页失效(面试常考“分页失效原因”),创建配置类:
package com.bailu12.springbootdemo.config;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration // 标识为配置类
public class MyBatisPlusConfig {// 配置分页插件@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 添加分页拦截器,指定数据库类型(MySQL)interceptor.addInnerInterceptor(new PaginationInnerInterceptor(com.baomidou.mybatisplus.core.metadata.TableInfoHelper.DB_TYPE_MYSQL));return interceptor;}
}
6. 接口测试(验证实战效果)
用Postman测试5个核心接口,确保CRUD正常、分页生效、逻辑删除正常:
-
新增:POST /user,请求体传入
-
删除:DELETE /user/1,返回true(数据库is_deleted变为1)
-
更新:PUT /user,请求体传入
-
单个查询:GET /user/2,返回对应用户信息(is_deleted=0)
-
分页查询:GET /user/page?pageNum=1&pageSize=5,返回分页数据(含总条数、当前页数据)
三、核心原理拆解(面试重点,吃透不踩坑)
今天的核心原理的有3个,都是面试高频考点,结合实战代码,拆解底层逻辑,拒绝死记硬背。
1. MyBatis-Plus整合SpringBoot的核心原理
MyBatis-Plus整合的核心是“自动配置”,延续SpringBoot“约定优于配置”的思想,底层依赖MyBatis-Plus的starter包,自动完成以下操作:
-
自动配置DataSource:读取application.properties中的数据库配置,创建数据源对象,注入IoC容器;
-
自动配置SqlSessionFactory:整合MyBatis核心配置,加载Mapper接口和映射文件,无需手动配置mybatis-config.xml;
-
自动扫描Mapper接口:通过@Mapper注解或@MapperScan注解,扫描指定包下的Mapper接口,将其注入IoC容器;
-
自动注入BaseMapper:BaseMapper封装了CRUD方法,底层通过动态代理生成代理对象,执行对应的SQL语句。
面试金句:MyBatis-Plus整合SpringBoot的核心是利用starter的自动配置,简化MyBatis的配置流程,通过BaseMapper和IService封装CRUD方法,减少重复代码,本质是对MyBatis的增强。
2. CRUD接口底层执行流程(以查询为例)
以getById(Long id)方法为例,拆解底层执行流程(面试常问“MyBatis-Plus的CRUD方法执行过程”):
-
调用userService.getById(id),IService的getById方法由ServiceImpl实现;
-
ServiceImpl调用baseMapper.selectById(id),BaseMapper的selectById方法是接口方法,由MyBatis-Plus动态生成代理实现;
-
代理对象解析方法名(selectById),生成对应的SQL语句(SELECT * FROM user WHERE id = ? AND is_deleted = 0);
-
通过SqlSessionFactory创建SqlSession,执行SQL语句,将结果集映射为User实体类对象;
-
返回User对象,完成查询操作。
3. 分页插件工作原理(面试高频)
MyBatis-Plus的分页插件本质是“SQL拦截器”,核心流程如下:
-
当执行分页查询(page方法)时,分页拦截器(PaginationInnerInterceptor)拦截SQL语句;
-
拦截器根据分页参数(pageNum、pageSize),对原SQL进行改造,添加LIMIT关键字(MySQL);
-
同时执行一条COUNT语句,查询总条数,封装到IPage对象中;
-
执行改造后的SQL,获取当前页数据,最终返回包含总条数、总页数、当前页数据的IPage对象。
易错点:分页失效的核心原因——未配置分页插件、分页参数传递错误、SQL语句手动写死LIMIT,覆盖了插件的改造逻辑。
四、面试高频真题汇总(附满分答案)
本期真题围绕CRUD、MyBatis-Plus整合、分页展开,都是后端面试高频题,直接背答案即可应对面试。
1. 基础必考题(入门级)
-
真题1:SpringBoot如何整合MyBatis-Plus?
答:1\. 导入MyBatis\-Plus starter、数据库驱动、lombok依赖;2\. 配置application\.properties,设置数据库连接和MyBatis\-Plus基础参数;3\. 创建实体类,使用MyBatis\-Plus注解指定表名、主键策略;4\. Mapper接口继承BaseMapper,添加@Mapper注解;5\. 配置分页插件(如需分页);6\. Service层继承IService,Controller层调用Service方法实现接口。 -
真题2:MyBatis和MyBatis-Plus的区别?
答:MyBatis\-Plus是MyBatis的增强工具,核心优势是“简化开发”:1\. 无需手动写CRUD SQL,BaseMapper/IService封装了基础方法;2\. 支持条件构造器(QueryWrapper),动态生成SQL;3\. 内置分页插件、逻辑删除、自动填充等功能;4\. 完全兼容MyBatis,可手动写SQL扩展。 -
真题3:@TableId的作用是什么?有哪些主键策略?
答:@TableId用于指定实体类的主键字段,主键策略(IdType)常用的有:AUTO(自增,MySQL常用)、INPUT(手动输入主键)、ASSIGN\_ID(雪花算法生成主键,分布式场景常用)。
2. 进阶面试题(高频重点)
-
真题1:MyBatis-Plus的逻辑删除原理是什么?
答:逻辑删除是通过@TableLogic注解标识删除字段(如is\_deleted),删除时不执行DELETE语句,而是执行UPDATE语句,将is\_deleted设为1;查询时自动添加AND is\_deleted = 0条件,实现“假删除”,保留数据历史。 -
真题2:MyBatis-Plus分页插件的工作原理?分页失效的原因有哪些?
答:工作原理:分页插件是SQL拦截器,拦截分页查询SQL,自动添加LIMIT关键字和COUNT查询,封装分页结果。分页失效原因:1\. 未配置分页插件;2\. 分页参数(pageNum、pageSize)传递错误;3\. 手动写死SQL的LIMIT,覆盖插件逻辑;4\. Mapper接口方法未使用MyBatis\-Plus的分页方法(如用了selectList而非page)。 -
真题3:MyBatis-Plus的自动填充原理是什么?
答:通过@TableField\(fill = FieldFill\.XXX\)注解标识需要自动填充的字段(如createTime、updateTime),自定义实现MetaObjectHandler接口,重写insertFill和updateFill方法,设置填充规则,当执行新增/更新操作时,自动为字段赋值,无需手动设置。
3. 实战避坑题(面试加分)
-
真题:使用MyBatis-Plus时,实体类字段与数据库表字段不一致,如何解决?
答:有3种方式:1\. 使用@TableField\(\&\#34;表字段名\&\#34;\)注解,指定实体类字段对应的数据表字段;2\. 在application\.properties中配置mybatis\-plus\.configuration\.map\-underscore\-to\-camel\-case=true(开启驼峰命名映射,如实体类username对应表user\_name);3\. 自定义SQL语句,指定字段映射。
五、总结+下期预告
本期核心总结
-
实战:完成了SpringBoot + MyBatis-Plus的整合,实现了用户模块CRUD接口,掌握了分页、逻辑删除、自动填充的使用;
-
原理:吃透了MyBatis-Plus整合原理、CRUD执行流程、分页插件工作机制,理解“增强MyBatis、简化开发”的核心;
-
面试:掌握了8道高频真题,重点记住分页、逻辑删除、自动填充的原理和实现方式,避免踩坑。
下期预告
SpringBoot学习第四天:接口异常处理+统一响应格式+拦截器实战,解决实战中接口报错混乱、响应格式不统一的问题,同时拆解异常处理原理、拦截器工作机制,配套面试高频题(如拦截器和过滤器的区别),让接口更规范、更健壮!
最后提醒:本期代码可直接复制到项目中运行,记得修改数据库配置,测试接口时注意逻辑删除的效果~ 有任何问题,评论区留言交流哦!
(注:文档部分内容可能由 AI 生成)
