SpringBoot之整合MyBatisPlus—简化CRUD操作的实战指南
在Java开发中,MyBatis作为主流ORM框架被广泛使用,但原生写XML映射文件或注解SQL仍需大量重复工作,MyBatisPlus(简称MP)在MyBatis基础上增强了CRUD操作,通过内置接口实现无SQL开发,大幅提升开发效率。
一、MyBatisPlus的优势
MyBatisPlus是一个MyBatis的增强工具,在保留MyBatis原有功能的基础上,提供了以下核心优势:
- 无SQL实现CRUD:内置
BaseMapper接口,提供单表增删改查操作,无需编写SQL; - 条件构造器:通过
QueryWrapper/LambdaQueryWrapper灵活组装查询条件,替代XML中的动态SQL; - 代码生成器:自动生成实体类、Mapper、Service等代码,减少重复劳动;
- 分页插件:一键集成分页功能,无需手动编写分页SQL;
- 全局主键策略:支持雪花算法、自增等多种主键生成策略,无需手动设置ID;
- 逻辑删除:通过注解实现逻辑删除(更新is_deleted字段),无需修改SQL。
MyBatisPlus与MyBatis的关系类似:MyBatisPlus = MyBatis + 增强功能,完全兼容MyBatis的用法,可平滑过渡。
二、环境准备
- JDK:1.8及以上;
- 开发工具:IntelliJ IDEA;
- 构建工具:Maven 3.6+;
- 数据库:MySQL 8.0;
- SpringBoot版本:2.7.x;
- MyBatisPlus版本:3.5.x。
三、SpringBoot整合MyBatisPlus步骤
3.1 创建项目并添加依赖
通过Spring Initializr创建项目,或手动添加以下依赖:
<!-- pom.xml核心依赖 -->
<dependencies>
<!-- SpringBoot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatisPlus起步依赖(包含MyBatis) -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok(简化实体类) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
注意:引入mybatis-plus-boot-starter后,无需再单独引入MyBatis的依赖,避免版本冲突。
3.2 配置数据库连接
在application.yml中配置数据源和MyBatisPlus参数:
# application.yml
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: root # 替换为实际密码
# MyBatisPlus配置
mybatis-plus:
# 实体类包路径(别名配置)
type-aliases-package: com.example.mp.entity
# 映射文件路径(若需要XML方式写SQL)
mapper-locations: classpath:mapper/*.xml
configuration:
# 日志打印(开发环境用)
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 驼峰命名转换(数据库字段user_name → 实体类userName)
map-underscore-to-camel-case: true
global-config:
db-config:
# 全局主键策略:ASSIGN_ID(雪花算法,分布式ID)
id-type: ASSIGN_ID
# 逻辑删除字段名(默认为is_deleted)
logic-delete-field: isDeleted
# 逻辑删除值(1:已删除)
logic-delete-value: 1
# 未删除值(0:未删除)
logic-not-delete-value: 0
3.3 创建数据库表
以user表为例,创建测试数据:
-- 创建数据库
CREATE DATABASE IF NOT EXISTS mp_demo CHARACTER SET utf8mb4;
USE mp_demo;
-- 创建用户表
CREATE TABLE user (
id BIGINT PRIMARY KEY COMMENT '主键ID',
username VARCHAR(50) NOT NULL COMMENT '用户名',
age INT COMMENT '年龄',
email VARCHAR(100) COMMENT '邮箱',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
is_deleted TINYINT DEFAULT 0 COMMENT '逻辑删除(0:未删,1:已删)'
) COMMENT '用户表';
-- 插入测试数据
INSERT INTO user (username, age, email) VALUES
('张三', 20, 'zhangsan@example.com'),
('李四', 22, 'lisi@example.com'),
('王五', 25, 'wangwu@example.com');
3.4 编写实体类(Entity)
使用Lombok和MyBatisPlus注解定义实体类:
package com.example.mp.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("user") // 指定数据库表名(若类名与表名一致可省略)
public class User {
// 主键(全局策略:雪花算法)
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String username;
private Integer age;
private String email;
// 自动填充:创建时间
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
// 自动填充:更新时间
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
// 逻辑删除字段
@TableLogic
private Integer isDeleted;
}
核心注解说明:
@TableName:指定实体类对应的数据库表名;@TableId:标记主键字段,type = IdType.ASSIGN_ID表示使用雪花算法生成ID;@TableField:标记普通字段,fill属性用于配置自动填充策略;@TableLogic:标记逻辑删除字段。
3.5 实现自动填充(元对象处理器)
为createTime和updateTime字段实现自动填充(创建时自动设置时间,更新时自动更新时间):
package com.example.mp.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component // 注册为Spring组件
public class MyMetaObjectHandler implements MetaObjectHandler {
// 插入时自动填充
@Override
public void insertFill(MetaObject metaObject) {
// 填充createTime和updateTime为当前时间
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
// 更新时自动填充
@Override
public void updateFill(MetaObject metaObject) {
// 填充updateTime为当前时间
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
3.6 创建Mapper接口
继承MyBatisPlus的BaseMapper接口,无需编写方法即可获得CRUD功能:
package com.example.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mp.entity.User;
// 继承BaseMapper<User>,泛型为实体类
public interface UserMapper extends BaseMapper<User> {
// 无需编写方法,BaseMapper已提供CRUD操作
}
3.7 注册Mapper接口
在SpringBoot启动类上添加@MapperScan注解,扫描Mapper接口所在包:
package com.example.mp;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.mp.mapper") // 扫描Mapper接口
public class MybatisPlusDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusDemoApplication.class, args);
}
}
3.8 编写Service层(可选)
MyBatisPlus提供IService和ServiceImpl简化Service层开发,比传统Service更高效:
3.8.1 定义Service接口
package com.example.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.mp.entity.User;
// 继承IService<User>
public interface UserService extends IService<User> {
// 可添加自定义业务方法
}
3.8.2 实现Service接口
package com.example.mp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import com.example.mp.service.UserService;
import org.springframework.stereotype.Service;
@Service
// 继承ServiceImpl<UserMapper, User>,实现UserService
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
// 无需编写基础CRUD方法,已由ServiceImpl实现
}
四、MyBatisPlus核心功能实战
4.1 基础CRUD操作(基于Service)
package com.example.mp.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.mp.entity.User;
import com.example.mp.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
// 1. 新增用户
@PostMapping
public String addUser(@RequestBody User user) {
boolean save = userService.save(user);
return save ? "新增成功,ID:" + user.getId() : "新增失败";
}
// 2. 根据ID查询
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getById(id);
}
// 3. 查询所有用户
@GetMapping
public List<User> getAllUsers() {
return userService.list();
}
// 4. 更新用户
@PutMapping
public String updateUser(@RequestBody User user) {
boolean update = userService.updateById(user);
return update ? "更新成功" : "更新失败";
}
// 5. 删除用户(逻辑删除)
@DeleteMapping("/{id}")
public String deleteUser(@PathVariable Long id) {
boolean remove = userService.removeById(id);
return remove ? "删除成功" : "删除失败";
}
}
测试结果:调用DELETE /users/1后,数据库中is_deleted字段会被更新为1(而非物理删除),查询时会自动过滤已删除数据。
4.2 条件查询(QueryWrapper/LambdaQueryWrapper)
使用条件构造器实现复杂查询,替代XML中的动态SQL:
@GetMapping("/query")
public List<User> queryUsers(
@RequestParam(required = false) String username,
@RequestParam(required = false) Integer minAge,
@RequestParam(required = false) Integer maxAge) {
// 方式1:QueryWrapper(字符串字段名,易出错)
/*QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (username != null) {
queryWrapper.like("username", username); // 模糊查询
}
if (minAge != null) {
queryWrapper.ge("age", minAge); // 大于等于
}
if (maxAge != null) {
queryWrapper.le("age", maxAge); // 小于等于
}
queryWrapper.orderByDesc("create_time"); // 按创建时间降序*/
// 方式2:LambdaQueryWrapper(类型安全,推荐)
LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>();
if (username != null) {
lambdaQuery.like(User::getUsername, username); // 引用方法,避免字段名拼写错误
}
if (minAge != null) {
lambdaQuery.ge(User::getAge, minAge);
}
if (maxAge != null) {
lambdaQuery.le(User::getAge, maxAge);
}
lambdaQuery.orderByDesc(User::getCreateTime);
return userService.list(lambdaQuery);
}
常用条件方法:
eq:等于(=);ne:不等于(≠);like:模糊查询(%value%);likeLeft:左模糊(%value);likeRight:右模糊(value%);ge:大于等于(≥);le:小于等于(≤);in:包含(in (value1, value2));orderByAsc/orderByDesc:排序。
4.3 分页查询
MyBatisPlus的分页插件可一键实现分页功能,步骤如下:
4.3.1 配置分页插件
package com.example.mp.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 {
// 注册分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件(针对MySQL)
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
4.3.2 实现分页查询
// 分页查询
@GetMapping("/page")
public IPage<User> getUserPage(
@RequestParam(defaultValue = "1") Integer pageNum, // 页码(默认1)
@RequestParam(defaultValue = "10") Integer pageSize) { // 每页条数(默认10)
// 创建分页对象
Page<User> page = new Page<>(pageNum, pageSize);
// 执行分页查询(支持条件查询)
IPage<User> userPage = userService.page(page,
new LambdaQueryWrapper<User>().ge(User::getAge, 18)); // 条件:年龄≥18
return userPage;
}
返回结果说明:
{
"records": [{"id": 1, "username": "张三", ...}], // 当前页数据
"total": 3, // 总条数
"size": 10, // 每页条数
"current": 1, // 当前页码
"pages": 1 // 总页数
}
4.4 自定义SQL(XML方式)
MyBatisPlus兼容MyBatis的XML方式,适合复杂SQL场景:
4.4.1 在Mapper接口中定义方法
package com.example.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mp.entity.User;
import java.util.List;
public interface UserMapper extends BaseMapper<User> {
// 自定义方法:查询指定年龄范围的用户
List<User> selectByAgeRange(@Param("minAge") Integer minAge, @Param("maxAge") Integer maxAge);
}
4.4.2 创建XML映射文件
在src/main/resources/mapper目录下创建UserMapper.xml:
<?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.mp.mapper.UserMapper">
<!-- 自定义查询:根据年龄范围查询 -->
<select id="selectByAgeRange" resultType="User">
SELECT * FROM user
WHERE age BETWEEN #{minAge} AND #{maxAge}
AND is_deleted = 0
</select>
</mapper>
4.4.3 在Service中调用
// 在UserService接口添加方法
List<User> selectByAgeRange(Integer minAge, Integer maxAge);
// 在UserServiceImpl实现方法
@Override
public List<User> selectByAgeRange(Integer minAge, Integer maxAge) {
return baseMapper.selectByAgeRange(minAge, maxAge);
}
// 在Controller中使用
@GetMapping("/age-range")
public List<User> getByAgeRange(Integer minAge, Integer maxAge) {
return userService.selectByAgeRange(minAge, maxAge);
}
五、MyBatisPlus高级特性
5.1 代码生成器(AutoGenerator)
AutoGenerator可根据数据库表自动生成实体类、Mapper、Service、Controller等代码,步骤如下:
5.1.1 添加代码生成器依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- 模板引擎(默认Velocity) -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
5.1.2 编写生成器配置类
package com.example.mp.generator;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import java.util.Collections;
public class CodeGenerator {
public static void main(String[] args) {
// 数据库连接配置
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai",
"root", "root")
// 全局配置
.globalConfig(builder -> {
builder.author("开发者") // 设置作者
.outputDir(System.getProperty("user.dir") + "/src/main/java") // 输出目录
.disableOpenDir(); // 生成后不打开文件夹
})
// 包配置
.packageConfig(builder -> {
builder.parent("com.example.mp") // 父包名
.moduleName("") // 模块名(无子模块可留空)
.mapper("mapper") // Mapper包名
.service("service") // Service包名
.controller("controller") // Controller包名
.entity("entity") // 实体类包名
.pathInfo(Collections.singletonMap(OutputFile.mapperXml,
System.getProperty("user.dir") + "/src/main/resources/mapper")); // MapperXML路径
})
// 策略配置
.strategyConfig(builder -> {
builder.addInclude("user") // 需要生成的表名(可多个)
.addTablePrefix("t_") // 忽略表前缀(如t_user → User)
// 实体类策略
.entityBuilder()
.enableLombok() // 启用Lombok
.enableTableFieldAnnotation() // 生成字段注解
// Controller策略
.controllerBuilder()
.enableRestStyle(); // 生成@RestController
})
.execute(); // 执行生成
}
}
运行CodeGenerator的main方法,即可自动生成全套代码。
5.2 乐观锁插件
乐观锁用于解决并发更新冲突(如秒杀场景),通过版本号控制:
5.2.1 配置乐观锁插件
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
5.2.2 在实体类添加版本字段
@Data
@TableName("user")
public class User {
// ... 其他字段
@Version // 标记为乐观锁版本字段
private Integer version;
}
5.2.3 使用乐观锁
// 更新时自动校验版本号
@PutMapping("/optimistic")
public String updateWithOptimisticLock(@RequestBody User user) {
// 1. 先查询用户(获取当前version)
User dbUser = userService.getById(user.getId());
if (dbUser == null) {
return "用户不存在";
}
// 2. 设置要更新的字段
dbUser.setUsername(user.getUsername());
dbUser.setAge(user.getAge());
// 3. 执行更新(MP会自动添加WHERE version = ?条件)
boolean success = userService.updateById(dbUser);
return success ? "更新成功" : "更新失败(可能已被其他线程修改)";
}
六、常见问题与避坑指南
6.1 依赖冲突问题
若同时引入mybatis-plus-boot-starter和mybatis-spring-boot-starter,会导致依赖冲突。解决方案:只保留mybatis-plus-boot-starter。
6.2 逻辑删除失效
原因:
- 未配置
@TableLogic注解; - 全局配置中
logic-delete-field与实体类字段名不一致; - 自定义SQL未添加
is_deleted = 0条件。
解决方案:确保注解和配置一致,自定义SQL需手动处理逻辑删除。
6.3 分页查询返回总数为0
原因:未配置分页插件或插件顺序错误。
解决方案:在MybatisPlusInterceptor中正确注册PaginationInnerInterceptor。
6.4 LambdaQueryWrapper字段引用错误
使用User::getUsername时若报“方法引用错误”,可能是Lombok版本与IDE插件不兼容。解决方案:升级Lombok版本或重新安装IDE插件。
总结
SpringBoot整合MyBatisPlus能显著简化CRUD操作:
- 无SQL开发:通过
BaseMapper和IService实现基础CRUD,无需编写SQL; - 条件构造器:
LambdaQueryWrapper替代动态SQL,类型安全且简洁; - 丰富插件:分页、乐观锁、逻辑删除等功能一键集成;
- 代码生成器:自动生成全套代码,减少重复劳动。
实际开发中,建议基础操作使用MyBatisPlus的内置方法,复杂查询结合XML方式,平衡开发效率和灵活性。
若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ

4267

被折叠的 条评论
为什么被折叠?



