MyBatis-Plus 详细学习文档
一、什么是 MyBatis-Plus
MyBatis-Plus (简称 MP) 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
1.1 主要特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete、update 操作智能分析阻断,也可自定义拦截规则,预防误操作
二、快速开始
2.1 环境准备
数据库 :MySQL 5.7+
JDK :1.8+
Maven :3.5+
Spring Boot:2.x
2.2 依赖引入
xml
<!-- MyBatis-Plus 依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
<!-- Lombok (可选,简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2.3 配置文件
yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/hmall?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: 123456
mybatis-plus:
mapper-locations: classpath*:mapper/**/*.xml # Mapper XML 文件位置
type-aliases-package: com.hmall.domain.po # 实体类包路径
configuration:
map-underscore-to-camel-case: true # 自动驼峰命名转换
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # SQL 日志打印
2.4 实体类
java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("cart") // 指定数据库表名
public class Cart {
@TableId(type = IdType.AUTO) // 主键自增
private Long id;
private Long userId;
private Long itemId;
private Integer num;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
2.5 Mapper 接口
java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hmall.domain.po.Cart;
import org.apache.ibatis.annotations.Mapper;
@Mapper // 标记为 MyBatis Mapper 接口
public interface CartMapper extends BaseMapper<Cart> {
// 无需编写任何 CRUD 方法,BaseMapper 已经提供
}
2.6 启动类配置
java
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.hmall.mapper") // 扫描 Mapper 接口所在包
public class CartServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CartServiceApplication.class, args);
}
}
2.7 测试使用
java
import com.hmall.domain.po.Cart;
import com.hmall.mapper.CartMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class CartMapperTest {
@Autowired
private CartMapper cartMapper;
@Test
public void testSelectList() {
// 查询所有记录
List<Cart> cartList = cartMapper.selectList(null);
cartList.forEach(System.out::println);
}
@Test
public void testInsert() {
// 插入记录
Cart cart = new Cart();
cart.setUserId(1L);
cart.setItemId(1001L);
cart.setNum(2);
int result = cartMapper.insert(cart);
System.out.println("插入结果:" + result);
System.out.println("插入的记录 ID:" + cart.getId()); // 自动填充 ID
}
}
三、核心功能
3.1 通用 CRUD 操作
MyBatis-Plus 的 BaseMapper 接口提供了丰富的 CRUD 方法,无需编写 SQL 即可完成大部分操作。
3.1.1 插入操作
java
// 插入单条记录
int insert(T entity);
// 批量插入记录
int insertBatchSomeColumn(Collection<T> entityList);
示例:
java
Cart cart = new Cart();
cart.setUserId(1L);
cart.setItemId(1001L);
cart.setNum(2);
cartMapper.insert(cart); // 返回影响行数
3.1.2 查询操作
java
// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件查询所有记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件查询所有记录(带分页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件查询总记录数
Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
示例:
java
// 根据 ID 查询
Cart cart = cartMapper.selectById(1L);
// 根据条件查询
QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", 1L)
.gt("num", 1); // user_id = 1 AND num > 1
List<Cart> cartList = cartMapper.selectList(queryWrapper);
3.1.3 更新操作
java
// 根据 ID 更新
int updateById(@Param(Constants.ENTITY) T entity);
// 根据 whereEntity 条件更新记录
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
示例:
java
// 根据 ID 更新
Cart cart = new Cart();
cart.setId(1L);
cart.setNum(5);
cartMapper.updateById(cart);
// 根据条件更新
UpdateWrapper<Cart> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("user_id", 1L)
.set("num", 10); // user_id = 1 的记录,num 字段更新为 10
cartMapper.update(null, updateWrapper);
3.1.4 删除操作
java
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 批量删除
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
示例:
java
// 根据 ID 删除
cartMapper.deleteById(1L);
// 根据条件删除
QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", 1L)
.lt("num", 1); // 删除 user_id = 1 且 num < 1 的记录
cartMapper.delete(queryWrapper);
3.2 条件构造器
MyBatis-Plus 提供了强大的条件构造器,用于构建复杂的查询条件。
3.2.1 QueryWrapper
用于构建查询条件。
示例:
java
QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
// WHERE user_id = 1 AND (num > 1 OR create_time > '2023-01-01')
queryWrapper.eq("user_id", 1L)
.and(w -> w.gt("num", 1).or().gt("create_time", "2023-01-01"))
.orderByDesc("create_time");
List<Cart> cartList = cartMapper.selectList(queryWrapper);
3.2.2 UpdateWrapper
用于构建更新条件。
示例:
java
UpdateWrapper<Cart> updateWrapper = new UpdateWrapper<>();
// WHERE user_id = 1 AND num > 5 SET num = num + 1
updateWrapper.eq("user_id", 1L)
.gt("num", 5)
.setSql("num = num + 1");
cartMapper.update(null, updateWrapper);
3.2.3 LambdaWrapper
使用 Lambda 表达式构建条件,避免字符串硬编码,更安全。
示例:
java
LambdaQueryWrapper<Cart> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// WHERE userId = 1L AND num > 1
lambdaQueryWrapper.eq(Cart::getUserId, 1L)
.gt(Cart::getNum, 1);
List<Cart> cartList = cartMapper.selectList(lambdaQueryWrapper);
3.3 分页查询
MyBatis-Plus 内置了分页插件,使用简单。
3.3.1 配置分页插件
java
import com.baomidou.mybatisplus.annotation.DbType;
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();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
3.3.2 使用分页查询
java
// 创建分页对象
Page<Cart> page = new Page<>(1, 10); // 当前页,每页大小
// 构建查询条件
QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", 1L);
// 执行分页查询
IPage<Cart> cartPage = cartMapper.selectPage(page, queryWrapper);
// 获取分页结果
List<Cart> records = cartPage.getRecords(); // 分页数据
long total = cartPage.getTotal(); // 总记录数
long pages = cartPage.getPages(); // 总页数
3.4 自动填充
MyBatis-Plus 支持自动填充功能,如创建时间、更新时间等字段。
3.4.1 实体类配置
java
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
@Data
@TableName("cart")
public class Cart {
// ... 其他字段
@TableField(fill = FieldFill.INSERT) // 插入时自动填充
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充
private LocalDateTime updateTime;
}
3.4.2 实现填充处理器
java
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 插入时自动填充
this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}
@Override
public void updateFill(MetaObject metaObject) {
// 更新时自动填充
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}
}
3.5 逻辑删除
MyBatis-Plus 支持逻辑删除,即标记删除而非物理删除。
3.5.1 实体类配置
java
import com.baomidou.mybatisplus.annotation.TableLogic;
@Data
@TableName("cart")
public class Cart {
// ... 其他字段
@TableLogic // 逻辑删除字段
private Integer deleted;
}
3.5.2 全局配置
yaml
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
使用:
java
// 逻辑删除操作(实际执行 UPDATE 语句,设置 deleted = 1)
cartMapper.deleteById(1L);
// 查询时自动过滤已删除记录(WHERE deleted = 0)
List<Cart> cartList = cartMapper.selectList(null);
四、高级功能
4.1 代码生成器
MyBatis-Plus 提供了强大的代码生成器,可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等代码。
4.1.1 引入依赖
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
4.1.2 代码生成示例
java
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import java.util.Collections;
public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/hmall?serverTimezone=UTC", "root", "123456")
.globalConfig(builder -> {
builder.author("yourName") // 设置作者
.outputDir(System.getProperty("user.dir") + "/src/main/java") // 设置输出目录
.disableOpenDir(); // 生成后不打开目录
})
.packageConfig(builder -> {
builder.parent("com.hmall") // 设置父包名
.moduleName("cart") // 设置模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "/src/main/resources/mapper")); // 设置 XML 输出目录
})
.strategyConfig(builder -> {
builder.addInclude("cart") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new VelocityTemplateEngine()) // 使用 Velocity 模板引擎
.execute();
}
}
4.2 自定义 SQL
虽然 MyBatis-Plus 提供了丰富的 CRUD 操作,但在某些复杂场景下仍需要编写自定义 SQL。
4.2.1 在 Mapper 接口中定义方法
java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hmall.domain.po.Cart;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface CartMapper extends BaseMapper<Cart> {
// 使用注解编写自定义 SQL
@Select("SELECT * FROM cart WHERE user_id = #{userId} AND num > #{minNum}")
List<Cart> selectByUserIdAndMinNum(Long userId, Integer minNum);
// 使用 XML 编写自定义 SQL
List<Cart> selectCustom(QueryWrapper<Cart> queryWrapper);
}
4.2.2 在 XML 文件中编写 SQL
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.hmall.cart.mapper.CartMapper">
<select id="selectCustom" resultType="com.hmall.cart.domain.po.Cart">
SELECT * FROM cart
<where>
<if test="ew != null">
${ew.customSqlSegment}
</if>
</where>
</select>
</mapper>
4.3 多表查询
MyBatis-Plus 本身不直接支持多表查询,但可以结合 MyBatis 的原生功能实现。
4.3.1 定义结果类
java
import lombok.Data;
@Data
public class CartItemVO {
private Long id;
private Long userId;
private Long itemId;
private String itemName;
private Double price;
private Integer num;
private LocalDateTime createTime;
}
4.3.2 编写多表查询 SQL
java
@Mapper
public interface CartMapper extends BaseMapper<Cart> {
@Select("SELECT c.id, c.user_id, c.item_id, i.name as item_name, i.price, c.num, c.create_time " +
"FROM cart c LEFT JOIN item i ON c.item_id = i.id " +
"WHERE c.user_id = #{userId}")
List<CartItemVO> selectCartWithItem(Long userId);
}
4.4 事务管理
MyBatis-Plus 结合 Spring 的事务管理功能,使用 @Transactional 注解即可实现事务控制。
示例:
java
@Service
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {
@Override
@Transactional // 开启事务
public void updateCart(Long userId, Long itemId, Integer num) {
// 1. 查询购物车记录
QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", userId)
.eq("item_id", itemId);
Cart cart = baseMapper.selectOne(queryWrapper);
if (cart != null) {
// 2. 更新数量
cart.setNum(num);
baseMapper.updateById(cart);
} else {
// 3. 新增记录
cart = new Cart();
cart.setUserId(userId);
cart.setItemId(itemId);
cart.setNum(num);
baseMapper.insert(cart);
}
// 如果发生异常,事务会自动回滚
}
}
五、最佳实践
5.1 实体类命名规范
- 实体类名与数据库表名保持一致,使用驼峰命名法
- 实体类字段与数据库列名保持一致,使用驼峰命名法(MyBatis-Plus 默认支持下划线转驼峰)
- 主键字段使用
@TableId注解标注 - 非数据库字段使用
@TableField(exist = false)注解标注
5.2 Mapper 接口命名规范
- Mapper 接口名以
Mapper结尾,如CartMapper - Mapper 接口继承
BaseMapper - 使用
@Mapper注解标注或在启动类使用@MapperScan扫描
5.3 Service 层命名规范
- Service 接口名以
I开头,以Service结尾,如ICartService - Service 实现类名以
Impl结尾,如CartServiceImpl - Service 实现类继承
ServiceImpl<Mapper, Entity>
5.4 条件构造器使用建议
- 优先使用
LambdaQueryWrapper和LambdaUpdateWrapper,避免字符串硬编码 - 复杂条件使用链式调用,保持代码清晰
- 使用
and()和or()方法构建复杂逻辑条件
5.5 分页查询建议
- 所有列表查询都使用分页,避免大数据量查询导致内存溢出
- 合理设置每页大小,建议不超过 100
- 使用
IPage接口获取分页信息,方便前端展示
5.6 性能优化建议
- 避免查询不需要的字段,使用
select()方法指定查询字段 - 使用
exists()代替count()进行存在性判断 - 批量操作使用
insertBatchSomeColumn()和updateBatchById()方法 - 合理使用索引,优化 SQL 执行效率
六、常见问题
6.1 主键生成策略选择
| 策略 | 描述 | 适用场景 |
|---|---|---|
IdType.AUTO |
数据库 ID 自增 | 适用于 MySQL、SQL Server 等支持自增的数据库 |
IdType.NONE |
无状态,该类型为未设置主键类型 | 不推荐使用 |
IdType.INPUT |
用户输入 ID | 需要用户手动设置 ID 的场景 |
IdType.ASSIGN_ID |
全局唯一 ID (雪花算法) | 适用于分布式系统,需要全局唯一 ID 的场景 |
IdType.ASSIGN_UUID |
全局唯一 ID (UUID) | 需要全局唯一字符串 ID 的场景 |
6.2 条件构造器常见错误
错误示例:
java
QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", userId)
.and(queryWrapper2 -> queryWrapper2.gt("num", 1).or().lt("num", 10));
正确示例:
java
QueryWrapper<Cart> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", userId)
.and(w -> w.gt("num", 1).or().lt("num", 10));
6.3 分页查询返回总记录数为 0
原因:
- 分页插件未正确配置
- 数据库用户没有查询权限
- SQL 语句中包含
LIMIT或OFFSET关键字
解决方案:
- 检查分页插件配置
- 确保数据库用户有查询权限
- 使用 MyBatis-Plus 提供的分页方法,避免手动添加分页关键字
6.4 自动填充不生效
原因:
- 实体类字段未添加
@TableField(fill = FieldFill.INSERT)或@TableField(fill = FieldFill.INSERT_UPDATE)注解 - 未实现
MetaObjectHandler接口或未添加@Component注解 - 填充处理器中的字段名与实体类字段名不一致
解决方案:
- 检查实体类注解配置
- 确保填充处理器已正确注册为 Spring 组件
- 检查填充处理器中的字段名是否正确
七、学习资源
- 官方文档:https://baomidou.com/pages/24112f/
- GitHub 仓库:https://github.com/baomidou/mybatis-plus
- 视频教程:B站搜索 "MyBatis-Plus 教程"
- 示例项目:https://gitee.com/baomidou/mybatis-plus-samples
八、总结
MyBatis-Plus 是一个功能强大的 MyBatis 增强工具,它大大简化了开发工作,提高了开发效率。通过本学习文档,你应该已经掌握了 MyBatis-Plus 的核心功能和使用方法。
在实际项目中,建议结合具体业务场景灵活使用 MyBatis-Plus 的各种功能,同时注意遵循最佳实践,编写高质量的代码。
MyBatis-Plus 的学习是一个持续的过程,建议多查阅官方文档,多实践,多总结,不断提高自己的使用水平。