MyBatis-Plus(简称 MP)作为 MyBatis 的增强工具,凭借 “只做增强不做改变” 的特性大幅简化了数据库操作开发流程,是 Java 后端面试中的高频考点。本文整理了 MP 核心面试题,并结合实战场景给出详细解答,帮助大家吃透 MP 核心知识点。

一、基础概念类

1. 什么是 MyBatis-Plus?它的核心优势是什么?

答:MyBatis-Plus 是基于 MyBatis 的增强工具,在 MyBatis 基础上仅做增强不做改变,旨在简化开发、提高效率。核心优势:

  • 无侵入:引入后不影响现有 MyBatis 工程逻辑;
  • 强大的 CRUD 封装:内置通用 Mapper/Service,无需手动编写单表 CRUD SQL;
  • 灵活的条件构造器:支持 Lambda 表达式编写查询条件,避免字段写错;
  • 主键自动生成:支持 4 种以上主键策略(含雪花算法);
  • 内置功能丰富:分页插件、性能分析插件、全局拦截插件、逻辑删除、乐观锁等;
  • 代码生成器:快速生成 Mapper/Model/Service/Controller 层代码。

2. MyBatis-Plus 支持哪些数据库?

答:支持所有兼容 MyBatis 且遵循标准 SQL 的数据库,常见的有 MySQL、Oracle、DB2、PostgreSQL、SQLServer 等;也适配国产数据库(达梦、人大金仓、神通等)。

3. MyBatis-Plus 和 MyBatis 的区别是什么?

特性MyBatisMyBatis-Plus
单表 CRUD需手动编写 SQL/XML内置通用 Mapper,无需手动编写
条件查询手动拼接 SQL条件构造器(Wrapper)+Lambda 表达式
分页需手动配置分页插件内置分页插件,支持多数据库
主键生成手动处理多种主键策略(自增、雪花算法、UUID 等)
逻辑删除 / 乐观锁需手动实现注解 + 配置即可实现

二、核心功能类

4. MyBatis-Plus 的主键生成策略有哪些?分别适用于什么场景?

答:MP 通过@TableId(type = IdType.XXX)指定主键策略,核心策略及场景:

  • AUTO:数据库自增,适用于单库、主键自增的场景;
  • INPUT:手动输入主键,适用于需自定义主键的场景;
  • ASSIGN_ID:雪花算法生成分布式唯一 ID(Long/String 类型),适用于分布式系统;
  • ASSIGN_UUID:生成 32 位 UUID,适用于主键为字符串且需全局唯一的场景;
  • NONE:未设置,跟随全局配置。

扩展:雪花算法核心逻辑 ——41 位毫秒数 + 10 位机器 ID(5 位数据中心 + 5 位机器)+12 位毫秒内流水号,保证分布式环境下 ID 唯一且趋势递增。

5. 如何实现 MyBatis-Plus 的分页功能?

答:实现步骤:

  1. 配置分页拦截器(3.4.0 + 版本):
    @Configuration
    public class MybatisPlusConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // 指定数据库类型(如MySQL)
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            return interceptor;
        }
    }
    
  2. 代码中使用 Page 对象分页:
    @Test
    public void testPage() {
        // 第2页,每页3条数据
        Page<User> page = new Page<>(2, 3);
        // 分页查询(条件为null表示无过滤)
        Page<User> userPage = userMapper.selectPage(page, null);
        // 获取分页结果
        List<User> records = userPage.getRecords(); // 分页数据
        long total = userPage.getTotal(); // 总条数
        long pages = userPage.getPages(); // 总页数
    }
    

6. 什么是逻辑删除?MyBatis-Plus 如何实现逻辑删除?

答:逻辑删除:并非物理删除数据库数据,而是通过字段标记数据状态(如deleted=0未删除、deleted=1已删除),避免数据丢失。MP 实现逻辑删除步骤:

  1. 数据库表添加逻辑删除字段(如deleted INT DEFAULT 0);
  2. 实体类字段添加@TableLogic注解:
    @TableLogic
    private Integer deleted;
    
  3. 配置文件指定逻辑删除值:
    mybatis-plus:
      global-config:
        db-config:
          logic-delete-value: 1 # 已删除值
          logic-not-delete-value: 0 # 未删除值
    

原理:执行delete操作时,MP 自动转换为update(如DELETE FROM user WHERE id=1 → UPDATE user SET deleted=1 WHERE id=1 AND deleted=0);查询时自动追加deleted=0条件。

7. MyBatis-Plus 的乐观锁实现原理是什么?如何配置?

答:乐观锁核心思想:假设数据不会被并发修改,仅在更新时检查版本号,若版本号一致则更新,否则更新失败(解决并发修改冲突)。MP 实现乐观锁步骤:

  1. 数据库表添加版本号字段(如version INT DEFAULT 0);
  2. 实体类字段添加@Version注解:
    @Version
    private Integer version;
    
  3. 配置乐观锁拦截器:
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 乐观锁拦截器
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        // 分页拦截器(若需分页可同时配置)
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
    

原理:更新时自动拼接版本号条件,且版本号自增(如UPDATE product SET price=1500, version=version+1 WHERE id=1 AND version=0)。

8. MyBatis-Plus 的条件构造器(Wrapper)有哪些常用用法?

答:Wrapper 是 MP 的条件构造器核心,常用实现类:QueryWrapper(查询)、UpdateWrapper(更新)、LambdaQueryWrapper(Lambda 风格)。常用 API 及场景:

// 1. 基础条件:姓名非空、邮箱非空、年龄≥12
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name")
       .isNotNull("email")
       .ge("age", 12);
List<User> list = userMapper.selectList(wrapper);

// 2. 等值查询:姓名=李四
wrapper.eq("name", "李四");
User user = userMapper.selectOne(wrapper);

// 3. 范围查询:年龄20~30
wrapper.between("age", 20, 30);
Integer count = userMapper.selectCount(wrapper);

// 4. 模糊查询:姓名不含"e"、邮箱以"t"开头
wrapper.notLike("name", "e")
       .likeRight("email", "t");

// 5. 排序+子查询:id倒序、id≥2
wrapper.orderByDesc("id")
       .inSql("id", "select id from user where id>=2");

// Lambda风格(避免字段写错)
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getName, "李四")
            .ge(User::getAge, 18);

9. MyBatis-Plus 如何实现自定义 SQL?

答:MP 支持保留 MyBatis 的自定义 SQL 能力,步骤:

  1. 配置文件指定 Mapper XML 路径:
    mybatis-plus:
      mapper-locations: classpath:mapper/*.xml # XML文件路径
      type-aliases-package: com.mp.entity # 实体类别名包
    
  2. Mapper 接口定义自定义方法:
    @Mapper
    public interface UserMapper extends BaseMapper<User> {
        // 自定义方法
        List<User> selectByAge(Integer age);
    }
    
  3. 编写 XML 文件(如UserMapper.xml):

    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.mp.mapper.UserMapper">
        <select id="selectByAge" resultType="User" parameterType="int">
            SELECT * FROM user WHERE age = #{age}
        </select>
    </mapper>
    
  4. 调用自定义方法:
    List<User> list = userMapper.selectByAge(18);
    

三、进阶应用类

10. MyBatis-Plus 的逻辑删除和物理删除的区别?实际项目中如何选择?

答:

  • 物理删除:直接删除数据库数据,不可恢复,适用于无保留价值的数据(如测试数据、临时数据);
  • 逻辑删除:仅标记删除状态,数据仍保留在库中,适用于需保留数据痕迹的场景(如用户、订单、商品等核心数据)。

选型建议:核心业务数据(订单、用户、交易记录)用逻辑删除;非核心临时数据用物理删除。

11. MyBatis-Plus 的乐观锁和悲观锁的区别?分别适用于什么场景?

特性乐观锁悲观锁
核心思想假设不会并发冲突,更新时校验假设会并发冲突,操作前加锁
实现方式版本号(MP 注解 + 拦截器)数据库锁(如 FOR UPDATE)
性能无锁,性能高加锁,性能较低
适用场景读多写少、并发冲突少写多读少、并发冲突多

示例场景:

  • 乐观锁:商品库存(读多写少,偶尔并发);
  • 悲观锁:金融交易(写多,需严格保证数据一致性)。

12. MyBatis-Plus 如何配置日志?为什么要配置日志?

答:配置日志可查看 MP 自动生成的 SQL 语句,便于调试和优化,配置方式(以控制台输出为例):

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

日志输出内容包含:执行的 SQL、参数、执行时间、结果集,可快速定位 SQL 拼接或条件查询的问题。

四、注解类

13. MyBatis-Plus 常用注解有哪些?分别作用是什么?

注解作用使用位置
@TableName指定实体类对应的数据表名实体类上
@TableId标记主键字段,指定主键策略实体类主键字段上
@TableField标记非主键字段(字段名映射、是否为数据库字段等)实体类非主键字段上
@TableLogic标记逻辑删除字段实体类逻辑删除字段上
@Version标记乐观锁版本号字段实体类版本号字段上
@EnumValue标记枚举类映射数据库的字段枚举类字段上

示例:

@TableName("sys_user") // 表名映射
public class User {
    @TableId(type = IdType.ASSIGN_ID) // 雪花算法主键
    private Long id;
    
    @TableField(value = "user_name") // 字段名映射(实体name → 表user_name)
    private String name;
    
    @TableLogic // 逻辑删除
    private Integer deleted;
    
    @Version // 乐观锁
    private Integer version;
}

五、实战场景类

14. 实际项目中,如何解决 MyBatis-Plus 实体类与数据库表字段名不一致的问题?

答:有 3 种解决方案:

  1. 注解映射:通过@TableName指定表名,@TableField(value = "字段名")指定列名;
  2. 全局配置字段前缀:若表字段统一带前缀(如sys_),可配置全局前缀:
    mybatis-plus:
      global-config:
        db-config:
          table-prefix: sys_ # 表前缀
    
  3. 开启下划线转驼峰(默认开启):MP 自动将数据库下划线字段(如user_name)映射为实体驼峰字段(userName)。

15. MyBatis-Plus 的分页插件为什么查询总条数为 0?如何排查?

答:常见原因及排查步骤:

  1. 未配置分页拦截器:检查MybatisPlusInterceptor是否注入,且指定了正确的数据库类型;
  2. 分页对象使用错误:需使用 MP 的Page对象,而非自定义分页类;
  3. 数据库权限问题:确认用户有查询COUNT(*)的权限;
  4. 条件构造器过滤了所有数据:检查 Wrapper 条件是否正确;
  5. 多表联查:自定义 SQL 分页需手动处理总条数(selectPage仅支持单表,多表需用selectMapsPage或手动拼接 COUNT SQL)。
Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐