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

相关推荐
青衫码上行2 小时前
maven依赖管理和生命周期
java·学习·maven
小六花s2 小时前
渗透测试前四天PHP文件包含笔记
android·学习·渗透测试
秋深枫叶红2 小时前
嵌入式第四十七篇——ARM汇编
汇编·arm开发·学习
rannn_1112 小时前
【Javaweb学习|Day7】事务管理、文件上传
后端·学习·javaweb
好奇龙猫2 小时前
大学院-筆記試験練習:数据库(データベース問題訓練) と 软件工程(ソフトウェア)(12)
学习
皮蛋sol周2 小时前
嵌入式学习数据结构(二)双向链表 内核链表
linux·数据结构·学习·嵌入式·arm·双向链表
代码游侠2 小时前
应用——基于 51 单片机的多功能嵌入式系统
笔记·单片机·嵌入式硬件·学习·51单片机
亦复何言??2 小时前
DreamWaQ - 基于隐式地形想象的鲁棒四足机器人运动学习
学习·机器人
后来后来啊3 小时前
2026.1.18学习笔记
笔记·学习