介绍
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:专门用于构造查询条件,支持基本的等于、不等于、大于、小于等各种常见操作。它允许你以链式调用的方式添加多个查询条件,并且可以组合使用
and
和or
逻辑。 - UpdateWrapper:用于构造更新条件,可以在更新数据时指定条件。与 QueryWrapper 类似,它也支持链式调用和逻辑组合。使用 UpdateWrapper 可以在不创建实体对象的情况下,直接设置更新字段和条件。
- LambdaQueryWrapper(推荐):这是一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下。
- LambdaUpdateWrapper:类似于 LambdaQueryWrapper,LambdaUpdateWrapper 是基于 Lambda 表达式的更新条件构造器。它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题。
常用方法示例
eq(R column, Object val)
: 等于 column = valne(R column, Object val)
: 不等于 column != valgt(R column, Object val)
: 大于 column > valge(R column, Object val)
: 大于等于 column >= vallt(R column, Object val)
: 小于 column < valle(R column, Object val)
: 小于等于 column <= vallike(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 NULLisNotNull(R column)
: column IS NOT NULLin(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 val2orderByAsc(R column)
: 升序排序 ORDER BY column ASCorderByDesc(R column)
: 降序排序 ORDER BY column DESClast(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
乐观锁插件(Optimistic Lock Plugin)
乐观锁是一种并发控制机制,用于确保在更新记录时,该记录未被其他事务修改。MyBatis-Plus 提供了 OptimisticLockerInnerInterceptor
插件,使得在应用中实现乐观锁变得简单。
实现
- 在数据库表中添加一个版本号字段(通常是version,类型为Integer或Long)
- 在实体类中对应属性上添加@Version注解:
@Version
private Integer version;
- 配置OptimisticLockerInnerInterceptor 插件:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 添加乐观锁插件
// ... 可以继续添加其他插件,如分页插件
return interceptor;
}
原理
乐观锁的实现通常包括以下步骤:
- 读取记录时,获取当前的版本号(version)。
- 在更新记录时,将这个版本号一同传递。
- 执行更新操作时,设置
version = newVersion
的条件为version = oldVersion
。 - 如果版本号不匹配,则更新失败。
逻辑删除(Logical Delete)
数据并不会真正从数据库中物理删除,而是通过一个字段来标记其为“已删除”状态
实现
-
在数据库表中添加一个逻辑删除标记字段(如 deleted,类型通常为 Integer 或 Boolean)。
-
在实体类中对应的属性上添加 @TableLogic 注解,并可以指定未删除值和已删除值。
@TableLogic @TableField(fill = FieldFill.INSERT) // 可选:插入时自动填充为未删除状态 private Integer deleted; // 0 表示未删除, 1 表示已删除
-
可以在全局配置文件中设置默认的未删除值和已删除值:
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 提供了一个便捷的自动填充功能,用于在插入或更新数据时自动填充某些字段,如创建时间、更新时间等。
实现
- 创建自定义的元数据处理器类,实现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);
}
}
- 在实体类的需要自动填充的属性上使用@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 # 开启驼峰命名转换
评论区
请登录后发表评论