SpringBoot之整合MyBatisPlus
SpringBoot之整合MyBatisPlus—简化CRUD操作的实战指南
- 一、MyBatisPlus的优势
- 二、环境准备
- 三、SpringBoot整合MyBatisPlus步骤
- 3.1 创建项目并添加依赖
- 3.2 配置数据库连接
- 3.3 创建数据库表
- 3.4 编写实体类(Entity)
- 3.5 实现自动填充(元对象处理器)
- 3.6 创建Mapper接口
- 3.7 注册Mapper接口
- 3.8 编写Service层(可选)
- 3.8.1 定义Service接口
- 3.8.2 实现Service接口
- 四、MyBatisPlus核心功能实战
- 4.1 基础CRUD操作(基于Service)
- 4.2 条件查询(QueryWrapper/LambdaQueryWrapper)
- 4.3 分页查询
- 4.3.1 配置分页插件
- 4.3.2 实现分页查询
- 4.4 自定义SQL(XML方式)
- 4.4.1 在Mapper接口中定义方法
- 4.4.2 创建XML映射文件
- 4.4.3 在Service中调用
- 五、MyBatisPlus高级特性
- 5.1 代码生成器(AutoGenerator)
- 5.1.1 添加代码生成器依赖
- 5.1.2 编写生成器配置类
- 5.2 乐观锁插件
- 5.2.1 配置乐观锁插件
- 5.2.2 在实体类添加版本字段
- 5.2.3 使用乐观锁
- 六、常见问题与避坑指南
- 6.1 依赖冲突问题
- 6.2 逻辑删除失效
- 6.3 分页查询返回总数为0
- 6.4 LambdaQueryWrapper字段引用错误
- 总结
在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: 8080spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghaiusername: rootpassword: root # 替换为实际密码# MyBatisPlus配置
mybatis-plus:# 实体类包路径(别名配置)type-aliases-package: com.example.mp.entity# 映射文件路径(若需要XML方式写SQL)mapper-locations: classpath:mapper/*.xmlconfiguration:# 日志打印(开发环境用)log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 驼峰命名转换(数据库字段user_name → 实体类userName)map-underscore-to-camel-case: trueglobal-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;// 逻辑删除字段@TableLogicprivate 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 {// 插入时自动填充@Overridepublic void insertFill(MetaObject metaObject) {// 填充createTime和updateTime为当前时间this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}// 更新时自动填充@Overridepublic 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 {@Autowiredprivate UserService userService;// 1. 新增用户@PostMappingpublic 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. 查询所有用户@GetMappingpublic List<User> getAllUsers() {return userService.list();}// 4. 更新用户@PutMappingpublic 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 {// 注册分页插件@Beanpublic 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)); // 条件:年龄≥18return 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 {@Beanpublic 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方式,平衡开发效率和灵活性。
若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ