MyBatis-Plus
# 总览
学习 MyBatis Plus 可以在掌握 MyBatis 基础的基础上更深入地了解和应用相关技术。以下是系统性地学习 MyBatis Plus 的整体学习思路和核心内容:
在学习的过程中就是要不断地寻找答案,带着问题学习才能更加高效
学习思路
- 掌握MyBatis基础知识:MyBatis Plus 是基于 MyBatis 构建的增强工具库,因此首先要确保对 MyBatis 的基本概念和使用有一定的了解。
- 学习MyBatis Plus文档:阅读官方文档和相关教程,了解 MyBatis Plus 的基本概念、核心特性以及使用方式。
- 快速上手:尝试使用 MyBatis Plus 进行快速的 CRUD 操作,了解其基本的使用方法和优势所在。
- 深入核心功能和特性:重点学习 MyBatis Plus 提供的常用功能,如代码生成器、条件构造器、分页插件、逻辑删除等核心功能,掌握其使用和优化方法。
- 阅读源码:根据实际需求深入阅读 MyBatis Plus 的源码,理解其实现原理,有助于更深入地理解其内部机制。
- 实际项目应用:在实际项目中应用 MyBatis Plus,尝试将其运用到项目中,并对比使用 MyBatis 相比使用 MyBatis Plus 的优势和不足。
核心需要理解的内容
对于一个资深开发工程师来说,在学习 MyBatis Plus 时需要重点理解以下内容:
- 代码生成器:了解如何利用代码生成器快速生成Entity、Mapper、Service、Controller等代码,加速项目开发。
- 条件构造器:深入了解 MyBatis Plus 提供的条件构造器的使用方法,掌握灵活的条件查询和更新方式。
- 分页插件:理解分页插件的原理和使用方法,以及在大数据量情况下的优化策略。
- 逻辑删除:了解如何使用 MyBatis Plus 的逻辑删除功能,并理解其实现原理。
- 多租户支持:学习 MyBatis Plus 提供的多租户支持功能,以及在实际多租户场景中的应用。
- 性能优化:了解在使用 MyBatis Plus 时的性能优化策略,例如 SQL 攻击防范、SQL 注入等安全方面的考虑。
- 与Spring等框架集成:学习 MyBatis Plus 与 Spring 等框架的集成方法,以及在实际项目中的最佳实践。
# 特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 通用 Service 主要用于封装通用的数据库操作方法,包括插入、更新、删除、查询等,以及一些通用的业务逻辑。它提供了一种更加面向对象的编程方式,使得 Service 层的代码更加简洁,并且提供了一些默认的实现,减少了重复代码的编写。
- 而 Mapper 则是直接与数据库打交道的组件,它提供了对数据库的原生操作能力,包括编写自定义的 SQL 语句、执行复杂的查询等。通过 Mapper,开发者可以更加灵活地使用数据库的功能,并且可以根据需要编写定制化的 SQL。
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
LambdaQueryWrapper
构造条件
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 自增、自定义填充插件、雪花算法、UUID
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 插件丰富:
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
# 核心功能
# 代码生成器
插件能够帮助我们,只需要点点点就好了,简单的很
# 其他注解功能
# 1、条件构造器
MyBatis-Plus的条件构造器是其提供的一个强大特性,它主要用于构建动态SQL语句,使得我们可以以链式调用的方式来书写查询条件,显著提高开发效率。以下是条件构造器(QueryWrapper和UpdateWrapper)的一些核心内容:
- 链式调用:MyBatis-Plus的条件构造器支持链式调用,可以连续拼接多个条件。
- 多种条件构造方法:条件构造器提供了丰富的方法来构造SQL中的where条件,例如:
eq
(等于),ne
(不等于),gt
(大于),ge
(大于等于),lt
(小于),le
(小于等于),between
(区间),notBetween
(非区间),like
(模糊查询),notLike
(非模糊查询),in
(在...中),notIn
(不在...中),isNull
(是null),isNotNull
(非null)等。
- 组合条件:条件构造器还支持将条件组合起来,实现复杂逻辑,如
and
、or
、nested
(嵌套条件)。 - 排序:通过
orderByAsc
和orderByDesc
方法可以实现对查询结果的排序。 - 指定查询字段:你可以使用
select
方法来指定需要查询的字段,减少数据传输量。 - 条件更新:除了
QueryWrapper
之外,还有UpdateWrapper
可用于构造条件更新,与QueryWrapper
用法相似,但主要应用于update
操作。 - 无条件删除:可以通过条件构造器来进行无条件的删除操作,虽然不常用,但在某些情况下很有用。
- 自定义SQL:还可以使用
apply
方法注入自定义SQL,以满足特殊的SQL需求,但要注意防止SQL注入风险。 - 实体作为条件构造器:可以直接传入一个实体对象,MyBatis-Plus会自动将非null的字段值作为等于条件,并且可以与其他条件组合使用。
十分便利的一点是,条件构造器的大部分方法都是重载的,可以接受额外的参数,例如条件是否加入。
通过上述功能,开发者可以减少大量的手写SQL语句的工作量,不仅代码更加优雅,同时也降低了出错的概率。总之,条件构造器是MyBatis-Plus中用于提高开发效率、实现复杂查询逻辑的一个重要工具。
# 分页插件Pagination
在开发实践中,利用MyBatis-Plus提供的分页插件能够极大简化分页的处理流程。以下是结合开发实际的MyBatis-Plus分页插件的总结:
- 插件集成:通过在Spring Boot项目中添加
mybatis-plus-boot-starter
依赖,分页插件通常会自动配置。如果需要手动添加或修改配置,可以通过定义PaginationInterceptor
Bean来实现。 - 基本用法:分页查询在 MyBatis-Plus 中非常直观,只需创建一个
Page
对象并传递给Mapper层的相关方法,即可实现分页查询。这避免了编写繁琐的分页SQL语句。 - 自定义分页:虽然MyBatis-Plus可以自动分页,但许多情况下你可能需要定制化分页逻辑,比如根据复杂的多表关联条件进行分页。在这种情况下,你可以通过MyBatis-Plus提供的方法进行手动分页操作,改写原有SQL以适应复杂场景。
- 优化考虑:在一些大数据量的场景中,分页查询的性能可能会变得非常关键。使用分页插件时,应考虑如何减少count查询次数,或者如何避免在大偏移量上使用LIMIT。MyBatis-Plus 分页插件允许关闭分页查询的总数统计(count)以提高性能,这可以通过设置
Page
对象上的searchCount
为false
来达成。 - 多租户考虑:在多租户的系统中,分页查询可能需要结合租户ID进行数据隔离。MyBatis-Plus允许通过实现
TenantLineInnerInterceptor
接口来实现多租户的分页数据隔离。 - 线程安全:在Web应用中,每个请求应当有自己独立的
Page
对象实例,以防止多个请求间共享Page对象导致的线程安全问题。 - 反馈给前端:在将分页信息反馈给前端时,通常会转换
Page
对象为DTO(Data Transfer Object),这样可以隐藏不必要的分页信息,同时确保灵活性和安全性。 - 自定义拦截:在MyBatis-Plus中,分页插件是一个拦截器。开发者可以实现自己的拦截器来扩展分页插件的功能,比如实现特定的日志记录、权限校验等。
总体而言,MyBatis-Plus 的分页插件为开发提供了便捷,但在实际应用中应当根据具体的业务需求和性能考量来调整分页策略。开发过程中要考虑分页功能的易用性、灵活性和性能,确保系统稳定高效地运行。
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.toolkit.ParameterUtils;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.SQLException;
/**
* 通用分页拦截器
* 当分页 size 小于 0 时, 直接设置为 0, 防止错误查询全表
*/
@Data
@NoArgsConstructor
public class GenericPaginationInnerInterceptor extends PaginationInnerInterceptor {
/**
* 数据库类型
* <p>
* 查看 {@link #findIDialect(Executor)} 逻辑
*/
private DbType dbType;
/**
* 方言实现类
* <p>
* 查看 {@link #findIDialect(Executor)} 逻辑
*/
private IDialect dialect;
public GenericPaginationInnerInterceptor(DbType dbType) {
this.dbType = dbType;
}
public GenericPaginationInnerInterceptor(IDialect dialect) {
this.dialect = dialect;
}
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
// 如果 size 小于 0,则设置为 0,避免错误查询全表
if (null != page && page.getSize() < 0) {
page.setSize(0);
}
super.beforeQuery(executor, ms, page, rowBounds, resultHandler, boundSql);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
外部拦截器调用:
public class MybatisPlusConfig implements WebMvcConfigurer {
// 。。。。。。。。。。。。。省略。。。。。。。。。。。。。。。。。。。。
/**
* 分页插件, 对于单一数据库类型来说,都建议配置该值,避免每次分页都去抓取数据库类型
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new GenericPaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13