MyBatis-Plus 详细学习文档

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 条件构造器使用建议

  • 优先使用 LambdaQueryWrapperLambdaUpdateWrapper,避免字符串硬编码
  • 复杂条件使用链式调用,保持代码清晰
  • 使用 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 语句中包含 LIMITOFFSET 关键字

解决方案

  • 检查分页插件配置
  • 确保数据库用户有查询权限
  • 使用 MyBatis-Plus 提供的分页方法,避免手动添加分页关键字

6.4 自动填充不生效

原因

  • 实体类字段未添加 @TableField(fill = FieldFill.INSERT)@TableField(fill = FieldFill.INSERT_UPDATE) 注解
  • 未实现 MetaObjectHandler 接口或未添加 @Component 注解
  • 填充处理器中的字段名与实体类字段名不一致

解决方案

  • 检查实体类注解配置
  • 确保填充处理器已正确注册为 Spring 组件
  • 检查填充处理器中的字段名是否正确

七、学习资源

八、总结

MyBatis-Plus 是一个功能强大的 MyBatis 增强工具,它大大简化了开发工作,提高了开发效率。通过本学习文档,你应该已经掌握了 MyBatis-Plus 的核心功能和使用方法。

在实际项目中,建议结合具体业务场景灵活使用 MyBatis-Plus 的各种功能,同时注意遵循最佳实践,编写高质量的代码。

MyBatis-Plus 的学习是一个持续的过程,建议多查阅官方文档,多实践,多总结,不断提高自己的使用水平。

相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛4 天前
计算机系统概论——校验码
学习
babe小鑫4 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms4 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下4 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。4 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J5 天前
从“Hello World“ 开始 C++
c语言·c++·学习