MyBatis-Plus讲解

MyBatisPlus是MyBatis的增强工具,提供极简开发、无侵入式增强和丰富功能,包括内置CRUD方法、条件构造器、分页插件、主键生成策略、逻辑删除和乐观锁等。通过BaseMapper接口可自动获得常用方法,支持Lambda表达式构建查询条件,并配备代码生成器提高效率。还提供自动填充、分页查询和乐观锁插件等实用功能,简化开发流程。

作者头像
LumiBee
78 天前 · 167 0
分享

介绍

MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

特性

  • 极简开发: 对MyBatis进行了大量的封装,尤其对单表操作,提供了丰富的内置的CRUD方法

  • 无侵入:只做增强不做改变,完全兼容原生MyBatis的所有功能.引入它不会对现有工程产生影响,甚至可以混用MyBatis-plus的功能和原生MyBatis XML配置

  • 强大的功能:除了基础的CRUD,还提供了诸如条件构造器,分页插件,主键生成策略,逻辑删除,乐观锁,性能分析插件等一系列实用功能

  • 高效便捷:通过代码生成器可以快速生成实体,Mapper,Service等代码,大大提高开发效率

核心概念与特性

BaseMapper<T> - 通用 CRUD 接口

这是MyBatis-Plus的精髓之一,只需要让Mapper接口继承BaseMapper<Entity>,就可以自动获得以下常用方法,而无需编写任何SQL:

  • insert(T entity): 插入一条记录。
  • deleteById(Serializable id): 根据主键 ID 删除记录。
  • deleteByMap(Map<String, Object> columnMap): 根据 columnMap 条件删除记录。
  • delete(Wrapper<T> wrapper): 根据 Wrapper 条件删除记录。
  • deleteBatchIds(Collection<? extends Serializable> idList): 批量删除。
  • updateById(T entity): 根据主键 ID 更新记录(实体中非 null 字段会参与更新)。
  • update(T entity, Wrapper<T> updateWrapper): 根据 Wrapper 条件更新记录。
  • selectById(Serializable id): 根据主键 ID 查询一条记录。
  • selectBatchIds(Collection<? extends Serializable> idList): 根据主键 ID 批量查询。
  • selectByMap(Map<String, Object> columnMap): 根据 columnMap 条件查询多条记录。
  • selectOne(Wrapper<T> queryWrapper): 根据 Wrapper 条件查询一条记录(结果多于 1 条会报错)。
  • selectCount(Wrapper<T> queryWrapper): 根据 Wrapper 条件查询总记录数。
  • selectList(Wrapper<T> queryWrapper): 根据 Wrapper 条件查询多条记录列表。
  • selectMaps(Wrapper<T> queryWrapper): 根据 Wrapper 条件查询多条记录,结果为 List<Map<String, Object>>。
  • selectObjs(Wrapper<T> queryWrapper): 根据 Wrapper 条件查询多条记录,只返回第一个字段的值。
  • selectPage(IPage<T> page, Wrapper<T> queryWrapper): 分页查询。
  • selectMapsPage(IPage<Map<String, Object>> page, Wrapper<T> queryWrapper): 分页查询,结果为 Map。

实体注解(Entity Annotations)

  • @TableName("table_name"): 指定实体类对应的数据库表名。如果表名与实体类名符合驼峰转下划线规则(如 UserInfo -> user_info),则可以省略。

  • @TableId(value = "column_name", type = IdType.xxx):主键相关的配置,如果主键名字就是id并且不需要主键策略可以省略

    • value: 指定数据库表的主键列名。

    • type: 指定主键策略:

      • IdType.AUTO: 数据库ID自增。

      • IdType.NONE: 未设置主键类型(由 MyBatis 自身或手动赋值).

      • IdType.INPUT: 用户手动输入ID。

      • IdType.ASSIGN_ID: 分配ID(主键类型为 Number(Long 和 Integer) 或 String),MP 默认使用雪花算法生成。

      • IdType.ASSIGN_UUID: 分配UUID(主键类型为 String).

  • @TableField(value = "column_name" , ...): 指定属性对应的数据库表字段名。当属性名与字段名不符合驼峰转下划线规则时使用。

    • exist = false: 表示该属性不是数据库表字段。

    • select = false: 查询时不返回该字段的值。

    • fill = FieldFill.xxx: 自动填充策略(如 INSERT, UPDATE, INSERT_UPDATE),需配合元数据处理器使用。

  • @Version: 标记乐观锁版本号字段。

  • @TableLogic: 标记逻辑删除字段(及其未删除值和已删除值)。

条件构造器(Wrapper)

​ MyBatis-Plus 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件.Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险(不支持多表关联查询)。

​ 在 MyBatis-Plus 中,Wrapper 类是构建查询和更新条件的核心工具。以下是主要的 Wrapper 类及其功能:

  • AbstractWrapper:这是一个抽象基类,提供了所有 Wrapper 类共有的方法和属性。它定义了条件构造的基本逻辑,包括字段(column)、值(value)、操作符(condition)等。所有的 QueryWrapper、UpdateWrapper、LambdaQueryWrapper 和 LambdaUpdateWrapper 都继承自 AbstractWrapper。
  • QueryWrapper:专门用于构造查询条件,支持基本的等于、不等于、大于、小于等各种常见操作。它允许你以链式调用的方式添加多个查询条件,并且可以组合使用 andor 逻辑。
  • UpdateWrapper:用于构造更新条件,可以在更新数据时指定条件。与 QueryWrapper 类似,它也支持链式调用和逻辑组合。使用 UpdateWrapper 可以在不创建实体对象的情况下,直接设置更新字段和条件。
  • LambdaQueryWrapper(推荐):这是一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下。
  • LambdaUpdateWrapper:类似于 LambdaQueryWrapper,LambdaUpdateWrapper 是基于 Lambda 表达式的更新条件构造器。它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题。

常用方法示例

  • eq(R column, Object val): 等于 column = val
  • ne(R column, Object val): 不等于 column != val
  • gt(R column, Object val): 大于 column > val
  • ge(R column, Object val): 大于等于 column >= val
  • lt(R column, Object val): 小于 column < val
  • le(R column, Object val): 小于等于 column <= val
  • like(R column, Object val): 模糊查询 column LIKE '%val%'
  • likeLeft(R column, Object val): 右模糊 column LIKE '%val'
  • likeRight(R column, Object val): 左模糊 column LIKE 'val%'
  • notLike(R column, Object val): column NOT LIKE '%val%'
  • isNull(R column): column IS NULL
  • isNotNull(R column): column IS NOT NULL
  • in(R column, Collection<?> coll): column IN (v1, v2, ...)
  • notIn(R column, Collection<?> coll): column NOT IN (v1, v2, ...)
  • between(R column, Object val1, Object val2): column BETWEEN val1 AND val2
  • orderByAsc(R column): 升序排序 ORDER BY column ASC
  • orderByDesc(R column): 降序排序 ORDER BY column DESC
  • last(String lastSql): 拼接 SQL 到最后(慎用,有 SQL 注入风险)
  • and(Consumer<LambdaQueryWrapper<T>> consumer): AND (子查询条件)
  • or(Consumer<LambdaQueryWrapper<T>> consumer): OR (子查询条件)
  • select(R... columns): 指定查询哪些字段。

更全的可以参考官网

分页插件(PaginationInnerInterceptor)

​ MyBatis-Plus 的分页插件 PaginationInnerInterceptor 提供了强大的分页功能,支持多种数据库,使得分页查询变得简单高效。

​ 如果想要使用插件还需要单独引入mybatis-plus-jsqlparser依赖:

<!-- jdk 11+ 引入可选模块 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-jsqlparser</artifactId>
</dependency>

<!-- jdk 8+ 引入可选模块 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
</dependency>

​ 可以通过Java配置来添加分页插件:

@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {

    /**
     * 添加分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); 					// 如果配置多个插件, 切记分页最后添加
        // 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
        return interceptor;
    }
}

属性介绍

PaginationInnerInterceptor 提供了以下属性来定制分页行为:

属性名 类型 默认值 描述
overflow boolean false 溢出总页数后是否进行处理
maxLimit Long 单页分页条数限制
dbType DbType 数据库类型
dialect IDialect 方言实现类

使用

在Service层或Controller层创建Page<T>对象,并将其作为参数传递给Mapper的selectPage方法

// Service 层
public Page<ArticleDTO> getArticles(long pageNum, long pageSize) {
  	//1. 创建分页请求对象(用于向Mapper传递分页参数)
    Page<Article> pageRequest = new Page<>(pageNum, pageSize); // pageNum 从1开始
    //2. 创建LambdaQueryWrapper用于构建查询条件
    LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
    wrapper.orderByDesc(Article::getGmtPublished);
  	//3. 执行分页查询,得到包含实体对象的分页结果
    // 返回的articleEntityPage包含了当前页Article实体列表以及总记录数,总页数等分页信息
    Page<Article> articleEntityPage = articleMapper.selectPage(pageRequest, wrapper);
    //4. 将 Page<Article> 转换为 Page<ArticleDTO>
    List<ArticleDTO> articleDTOList = new ArrayList<>();
  	    if (articleEntityPage.getRecords() != null && !articleEntityPage.getRecords().isEmpty()) {
        for (Article article : articleEntityPage.getRecords()) {
            // 这里假设有一个 convertToArticleDTO(Article article) 的方法
            // 这个方法负责将单个 Article 实体转换为 ArticleDTO,
            // 可能包括处理作者信息、日期格式化等。
            ArticleDTO dto = convertToArticleDTO(article); // 需要实现这个转换方法
            articleDTOList.add(dto);
        }
    }

    //5. 创建并填充最终返回的 Page<ArticleDTO> 对象
    // 参数1: articleEntityPage.getCurrent() - 获取原始分页结果的当前页码
    // 参数2: articleEntityPage.getSize() - 获取原始分页结果的每页大小
    // 参数3: articleEntityPage.getTotal() - 获取原始分页结果的总记录数
    Page<ArticleDTO> dtoPage = new Page<>(articleEntityPage.getCurrent(), articleEntityPage.getSize(), articleEntityPage.getTotal());
    //6. 设置转换后的DTO列表到新的分页对象中
    dtoPage.setRecords(articleDTOList);
    //7. 设置总页数到新的分页对象中
    dtoPage.setPages(articleEntityPage.getPages());
    return dtoPage;
}

Page类

Page 类继承了 IPage 类,实现了简单分页模型。如果你需要实现自己的分页模型,可以继承 Page 类或实现 IPage 类。

属性名 类型 默认值 描述
records List emptyList 查询数据列表
total Long 0 查询列表总记录数
size Long 10 每页显示条数,默认 10
current Long 1 当前页
orders List emptyList 排序字段信息
optimizeCountSql boolean true 自动优化 COUNT SQL
optimizeJoinOfCountSql boolean true 自动优化 COUNT SQL 是否把 join 查询部分移除
searchCount boolean true 是否进行 count 查询
maxLimit Long 单页分页条数限制
countId String XML 自定义 count 查询的 statementId

乐观锁插件(Optimistic Lock Plugin)

​ 乐观锁是一种并发控制机制,用于确保在更新记录时,该记录未被其他事务修改。MyBatis-Plus 提供了 OptimisticLockerInnerInterceptor 插件,使得在应用中实现乐观锁变得简单。

实现

  1. 在数据库表中添加一个版本号字段(通常是version,类型为Integer或Long)
  2. 在实体类中对应属性上添加@Version注解:
@Version
private Integer version;
  1. 配置OptimisticLockerInnerInterceptor 插件:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 添加乐观锁插件
    // ... 可以继续添加其他插件,如分页插件
    return interceptor;
}

原理

乐观锁的实现通常包括以下步骤:

  1. 读取记录时,获取当前的版本号(version)。
  2. 在更新记录时,将这个版本号一同传递。
  3. 执行更新操作时,设置 version = newVersion 的条件为 version = oldVersion
  4. 如果版本号不匹配,则更新失败。

逻辑删除(Logical Delete)

​ 数据并不会真正从数据库中物理删除,而是通过一个字段来标记其为“已删除”状态

实现

  1. 在数据库表中添加一个逻辑删除标记字段(如 deleted,类型通常为 Integer 或 Boolean)。

  2. 在实体类中对应的属性上添加 @TableLogic 注解,并可以指定未删除值和已删除值。

    @TableLogic
    @TableField(fill = FieldFill.INSERT) // 可选:插入时自动填充为未删除状态
    private Integer deleted; // 0 表示未删除, 1 表示已删除
    
  3. 可以在全局配置文件中设置默认的未删除值和已删除值:

    mybatis-plus:
     global-config:
      db-config:
       logic-delete-field: deleted # 全局逻辑删除的实体字段名
       logic-delete-value: 1 # 逻辑已删除值(默认为 1)
       logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
    

原理

  • 执行删除操作 (deleteById, delete 等) 时,MP 会自动将其转换为更新操作,将逻辑删除字段的值更新为“已删除”状态。
  • 执行查询操作 (selectById, selectList 等) 时,MP 会自动在 WHERE 条件中加入逻辑删除字段的过滤条件,只查询出“未删除”状态的数据。
  • 如果确实需要查询包括已逻辑删除的数据,可以使用特定的 Wrapper 方法或自定义 SQL。

自动填充(Automatic Filling)

​ MyBatis-Plus 提供了一个便捷的自动填充功能,用于在插入或更新数据时自动填充某些字段,如创建时间、更新时间等。

实现

  1. 创建自定义的元数据处理器类,实现MetaObjectHandler接口,并重写insertFill和updateFill方法:
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        // metaObject:元对象,可以获取到原始实体对象及其属性
        // "gmtCreate" 和 "gmtModified" 是实体类中的属性名
        this.strictInsertFill(metaObject, "gmtCreate", LocalDateTime::now, LocalDateTime.class);
        this.strictInsertFill(metaObject, "gmtModified", LocalDateTime::now, LocalDateTime.class);
        // 假设有 deleted 字段,并希望插入时默认为0 (未删除)
        this.strictInsertFill(metaObject, "deleted", () -> 0, Integer.class);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "gmtModified", LocalDateTime::now, LocalDateTime.class);
    }
}
  1. 在实体类的需要自动填充的属性上使用@TableField(fill = FieldFill.xxx)注解
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;

@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;

@TableField(fill = FieldFill.INSERT)
private Integer deleted;

FieldFill 枚举可选值:

  • DEFAULT: 默认不处理。
  • INSERT: 插入时填充。
  • UPDATE: 更新时填充。
  • INSERT_UPDATE: 插入和更新时都填充。

代码生成器(AutoGenerator)

​ MyBatis-Plus 提供了一个强大的代码生成器模块 (mybatis-plus-generator),可以根据数据库表结构自动生成 Entity、Mapper 接口、Mapper XML、Service 接口、Service 实现类甚至 Controller 类的基础代码。

  • 使用方式:通常是创建一个单独的 Java 类,在 main 方法中配置 AutoGenerator 的各种策略(数据源配置、全局配置、包配置、模板配置、策略配置等),然后运行该类即可生成代码。
  • 优点:极大减少了手动创建这些样板代码的工作量,尤其是在项目初期或表结构较多时,能显著提高开发效率。
  • 配置项丰富:可以自定义生成的代码风格、父类、是否覆盖已有文件、表名/字段名转换规则等。

配置

具体的安装可以参考官网的教程

application.yml相关配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/your_database?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml # 如果有自定义XML
  type-aliases-package: com.example.entity # 实体类别名包
  global-config:
    db-config:
      id-type: assign_id # 全局主键策略
      # logic-delete-field: deleted # 全局逻辑删除字段名 (如果配置了)
      # logic-delete-value: 1 # 全局逻辑删除值 (如果配置了)
      # logic-not-delete-value: 0 # 全局逻辑未删除值 (如果配置了)
    banner: false # 关闭MP启动时的banner信息
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志
    map-underscore-to-camel-case: true # 开启驼峰命名转换

阅读量: 167

评论区

登录后发表评论

正在加载评论...
相关阅读

Redis04_数据类型

# 五大数据类型 ![image-20250506173728272](https://p.ipic.vip/iq69f6.png) ## Redis-Key ```bash 127.0....

91
0

Elasticsearch 入门指南

# Elasticsearch 入门指南:从零到一构建搜索引擎 在成功部署了 Elasticsearch 和 Kibana 后,如果还有不会安装的可以查看这篇[文章](https://hive...

188
1

Spring Security实战-构建安全的Web应用

Spring Security 作为 Spring 生态系统中不可或缺的一员,提供了一套全面且可扩展的机制来处理身份验证和授权。本文将结合实际应用场景,深入剖析 Spring Security ...

138
0

Redis07_Redis.conf详解

# Redis.conf详解 ## 定义 `redis.conf` 是 Redis 服务器启动时加载的配置文件。它包含了 Redis 运行时的各种参数和选项,通过修改这个文件,可以自定义 R...

125
0

Elasticsearch 相关性控制的艺术

# Elasticsearch 相关性控制的艺术 我们可以通过控制相关性的得分使得用户最想要的结果排在最前面。本文所举的例子依旧是建设博客的搜索引擎。 ## 1. boosting 查询 ...

114
1