Mybatis Plus使用手册
Mybatis Plus是什么?
MyBatis-Plus (简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
MyBatis Plus提供了以下功能和特点:
- CRUD操作的简化:MyBatis Plus通过提供一系列的通用方法,简化了数据库的增、删、改、查操作的编写。开发人员不再需要手动编写大量的SQL语句,可以通过简单的方法调用完成常见的数据库操作。
- 自动生成代码:MyBatis Plus支持代码生成工具,可以根据数据库表结构自动生成实体类、Mapper接口以及对应的XML映射文件,极大地减少了手动编写代码的工作量。
- 分页查询的支持:MyBatis Plus提供了强大的分页查询功能,可以方便地进行分页查询,并支持各种分页参数的设置和灵活的分页查询结果处理。
- 条件构造器:MyBatis Plus引入了条件构造器的概念,可以通过使用条件构造器来动态地拼接SQL查询条件,避免了手动编写复杂的SQL语句,提高了查询的灵活性。
- 乐观锁和逻辑删除:MyBatis Plus提供了对乐观锁和逻辑删除的支持。通过简单的注解或配置,可以实现数据的版本控制和逻辑删除功能,提高了数据的安全性和可靠性。
- 自动生成主键:MyBatis Plus支持主键自动生成策略,可以自动生成主键值,并将其回写到实体对象中,简化了主键生成的处理过程。
总之,MyBatis Plus是一个强大而简便的MyBatis增强工具库,可以加速开发人员对数据库的访问和操作,减少重复的编码工作,提高开发效率,并提供了一些高级功能来处理常见的数据库操作需求。
快速使用
1. 引入依赖
Spring Boot
只需要引入依赖即可
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>最新版本</version>
</dependency>Spring
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>最新版本</version>
</dependency>需要注意的是,引入Mybatis-plus后,不需要再引入mybatis或spring-mybatis,以避免版本差异。
2. 配置Mybatis-plus
Spring Boot工程
在Springboot启动类添加 MapperScan 注解
@SpringBootApplication
@MapperScan("com.chenzhuowen.mybatisplusstudy.mapper")
public class MybatisPlusStudyApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusStudyApplication.class, args);
}
}Spring工程
在配置文件中,配置MapperScan
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.baomidou.mybatisplus.samples.quickstart.mapper"/>
</bean>在配置文件中,配置sqlSessionFactory为mybatisplus的com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>常用注解
@TableName:表名注解,注解在类上,标识实体类对应的数据表。@TableId:主键注解,注解在类属性上,标识类属性为数据表主键。idType:主键属性。可以填入以下属性:AUTO:数据库 ID 自增。NONE:无状态,跟随全局设置,约等于INPUT。INPUT:insert前自行set主键值。ASSIGN_ID:分配id,使用接口IdentifierGenerator的nextId方法获取id。ASSIGN_UUID:分配UUID,使用接口IdentifierGenerator的方法nextUUID
@TableField:字段注解,注解在类属性上,标识属性对应的数据表字段。@Version:乐观锁注解,注解在字段上。@EnumValue:普通枚举类注解,注解在枚举值字段上。@TableLogic:注解在类属性上,表字段逻辑处理注解(逻辑删除)。@KeySequence:注解在类上,配置序列主键策略,接收序列名作为参数。@InterceptorIgnore:注解在Mapper类上或Mapper的方法上。用于开启关闭一些插件功能。@OrderBy:注解在类属性上,指定内置sql的默认排序规则,优先级低于Wrapper。
功能特性
概览
MybatisPlus提供了如下功能特性:
- 快速测试
- 代码生成器
- Mapper CRUD接口
- Service CRUD接口
- ActiveRecord实体类调用模式
- Db静态类调用模式
- SimplateQuery流处理工具类
- 条件构造器
- 主键策略
- 自定义ID生成器
- 逻辑删除
- 自动填充
- SQL注入器
- 执行SQL分析打印
- 数据安全保护
- 多数据源
- MybatisX快速开发插件
- 企业高级特性
- 脚本自动维护
快速测试
mybatis-plus提供了快速测试依赖,只需要添加测试相关依赖,并使用@MybatisPlusTest注解快速配置测试类。
仅适用于Mapper类快速测试,不会启动springboot的组件扫描,即不会注入自定义bean。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter-test</artifactId>
<version>3.5.3.1</version>
</dependency>测试示例
@MybatisPlusTest
public class FastSysUserMapperTest {
@Autowired
SysUserMapper sysUserMapper;
@Test
void testInsert() {
SysUser chenzhuowen = SysUser.builder().age(12).email("512745183@qq.com").name("chenzhuowen").build();
sysUserMapper.insert(chenzhuowen);
System.out.println(chenzhuowen.getId());
SysUser sysUser = sysUserMapper.selectById(chenzhuowen.getId());
System.out.println(sysUser.toString());
}
}代码生成器
需要设置4部分内容:
- 全局配置
- 数据源配置
- 生成包及xml配置
- 数据库表策略配置
MapperCRUD接口
MybatisPlus为我们提供了Mapper层CRUD接口,封装了BaseMapper接口,具体有如下api:
insert:插入记录Delete:删除记录Update:更新记录Select:查询记录
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
}@MybatisPlusTest
public class SysUserMapperTest {
@Autowired
SysUserMapper sysUserMapper;
@Test
public void testInsert() {
SysUser chenzhuowen = SysUser.builder().age(12).email("512745183@qq.com").name("chenzhuowen").build();
sysUserMapper.insert(chenzhuowen);
System.out.println(chenzhuowen.getId());
SysUser sysUser = sysUserMapper.selectById(chenzhuowen.getId());
System.out.println(sysUser.toString());
}
@Test
public void testDelete() {
int i = sysUserMapper.deleteById(1l);
if (i > 0) {
System.out.println("delete success");
} else {
System.out.println("delete fail");
}
}
@Test
public void testUpdate() {
SysUser user1 = SysUser.builder().age(11).id(1L).build();
int i = sysUserMapper.updateById(user1);
if (i > 0) {
SysUser sysUser = sysUserMapper.selectById(1);
System.out.println(sysUser.toString());
} else
System.out.println("update fail");
}
@Test
public void testSelect() {
SysUser sysUser2 = sysUserMapper.selectById(2);
System.out.println(sysUser2.toString());
}
@Test
public void testSelectByWrapper() {
List<SysUser> sysUsers = sysUserMapper.selectList(
Wrappers.<SysUser>query()
.eq("id", Integer.valueOf(1)));
sysUsers.stream().forEach(System.out::println);
List<Map<String, Object>> sysUserMaps = sysUserMapper.selectMaps(Wrappers.<SysUser>query().eq("id", 1)
.select("id", "age", "name"));
sysUserMaps.stream().forEach(m -> m.forEach((s, o) -> System.out.println("key:" + s + " value:" + o)));
}
}Mapper选装件
选装件位于 com.baomidou.mybatisplus.extension.injector.methods 包下 需要配合Sql 注入器使用。
- AlwaysUpdateSomeColumnById
- insertBatchSomeColumn
- logicDeleteByIdWithFill
Service CRUD接口
MybatisPlus为我们提供了service层CRUD接口,封装了IService接口。为避免与Mapper层混淆,Service层定义了一套方法前缀名称,具体有如下api:
get:查询单行list:查询集合remove:删除save:插入记录page:分页saveOrUpdate:插入或更新count:查询记录数
链式操作
query:链式查询update:链式更新
@Service
public class SysUserService extends ServiceImpl<SysUserMapper, SysUser> {
}@SpringBootTest
class SysUserServiceTest {
@Autowired
SysUserService sysUserService;
@Test
public void testGet() {
SysUser sysUser = sysUserService.getById(1L);
System.out.println(sysUser.toString());
}
@Test
public void testList() {
List<SysUser> list = sysUserService.list();
list.forEach(System.out::println);
}
@Test
public void testRemove() {
sysUserService.removeById(1);
List<SysUser> list = sysUserService.list();
list.forEach(System.out::println);
}
@Test
public void testUpdate() {
SysUser chenzhuowen = SysUser.builder().id(1L).name("chenzhuowen").age(28).email("512745183@qq.com").build();
sysUserService.updateById(chenzhuowen);
List<SysUser> list = sysUserService.list();
list.forEach(System.out::println);
}
@Test
public void testCount() {
long count = sysUserService.count();
System.out.println("count = " + count);
}
@Test
public void testPage() {
Page<SysUser> page = sysUserService.page(Page.of(1, 3));
List<SysUser> records = page.getRecords();
records.forEach(System.out::println);
}
}ActiveRecord实体类调用模式
- 实体类只需继承
Model类即可进行强大的 CRUD 操作 - 需要项目中已注入对应实体的
BaseMapper
使用
Model类的CRUD接口时,实体类的属性会作为查询条件使用,返回的是新对象。ActiveRecord模式使用起来违反了类单一原则,不建议使用。
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysUser extends Model<SysUser> {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@TableField
private String name;
private Integer age;
private String email;
}@MybatisPlusTest
public class ActiveRecordTest {
@Test
public void activeRecordSelectTest() {
SysUser sysUser = new SysUser();
List<SysUser> sysUsers = sysUser.selectAll();
sysUsers.forEach(System.out::println);
}
@Test
public void activeRecordInsertTest() {
SysUser chenzhuowen = SysUser.builder().name("chenzhuowen").age(27).email("512745183@qq.com").build();
if (chenzhuowen.insert()) {
System.out.println("inset success");
} else {
System.out.println("insert failure");
}
chenzhuowen.selectAll().forEach(System.out::println);
}
@Test
public void activeRecordDeleteTest() {
SysUser sysUser = new SysUser();
sysUser.setId(1L);
sysUser.deleteById();
List<SysUser> sysUsers = sysUser.selectAll();
sysUsers.forEach(System.out::println);
}
@Test
public void activeRecordUpdateTest() {
SysUser sysUser = new SysUser();
sysUser.setId(1L);
sysUser.setName("new Name");
sysUser.insertOrUpdate();
List<SysUser> sysUsers = sysUser.selectAll();
sysUsers.forEach(System.out::println);
}
}Db类静态调用模式
MP提供Db类实现静态调用的方式执行CRUD方法,避免Spring环境下Service循环注入、简洁代码,提升效率。
@MybatisPlusTest
public class DbTest {
@Test
public void dbListTest() {
List<SysUser> list = Db.list(SysUser.class);
list.forEach(System.out::println);
}
@Test
public void dbSaveTest() {
SysUser chenzhuowen = SysUser.builder().name("chenzhuowen").age(27).email("512745183@qq.com").build();
Db.save(chenzhuowen);
List<SysUser> list = Db.list(SysUser.class);
list.forEach(System.out::println);
}
@Test
public void dbDeleteTest() {
boolean b = Db.removeById(1, SysUser.class);
List<SysUser> list = Db.list(SysUser.class);
list.forEach(System.out::println);
}
@Test
public void dbUpdateTest() {
SysUser chenzhuowen = SysUser.builder().id(1L).name("chenzhuowen").age(27).email("512745183@qq.com").build();
boolean b = Db.updateById(chenzhuowen);
List<SysUser> list = Db.list(SysUser.class);
list.forEach(System.out::println);
}
}SimpleQuery工具类
SimpleQuery工具类可以对查询后的结果用Stream流进行一系列处理,使其返回指定结果,简介了api的调用。
@SafeVarargs
public static <E, A> List<A> list(LambdaQueryWrapper<E> wrapper, SFunction<E, A> sFunction, Consumer<E>... peeks) {
return list2List(Db.list(wrapper.setEntityClass(getType(sFunction))), sFunction, peeks);
}条件构造器
MP为我们提供了三个类用于构造sql语句
- 虚基类
AbstractWrapper:提供了各种api构造sql的where部分。 - 实体类
QueryWrapper:继承自AbstractWrapper。提供了select方法,用于设置select查询字段。 - 实体类
UpdateWrapper:继承自UpdateWrapper。提供了set方法用于设置set字段,提供setSql方法用于设置set部分SQL。
使用Wrappers工具类可以方便地创建QueryWrapper、UpdateWrapper等。
List<SysUser> sysUsers = sysUserMapper.selectList(Wrappers.<SysUser>query().eq("id",Integer.valueOf(1)));
sysUsers.stream().forEach(System.out::println);使用条件构造器,构造select字段和where条件。
@Test
public void testSelectByWrapper() {
List<SysUser> sysUsers = sysUserMapper.selectList(
Wrappers.<SysUser>query()
.eq("id", Integer.valueOf(1)));
sysUsers.stream().forEach(System.out::println);
List<Map<String, Object>> sysUserMaps = sysUserMapper.selectMaps(Wrappers.<SysUser>query().eq("id", 1)
.select("id", "age", "name"));
sysUserMaps.stream().forEach(m -> m.forEach((s, o) -> System.out.println("key:" + s + " value:" + o)));
}主键策略
使用@TableId注解类字段,标识字段与数据库主键对应,type属性标识主键id生成策略。
主键生成策略有如下:
AUTO:数据库ID自增,需要保证数据库设置了ID自增。NONE:该类型为未设置主键类型(注解里等于跟随全局,全局里约等于INPUT)INPUT:用户输入ID。该类型可以通过自己注册自动填充插件进行填充。使用@KeySequence注解类,可以配置序列名,设置主键生成策略。ASSIGN_ID:只有当插入对象id为空时,才自动填充,要求主键类型为Number或String。默认实现为雪花算法。ASSIGN_UUID:只有当插入对象id为空时,才自动填充,要求主键类型为String。默认实现为UUID。
@KeySequence(value = "SEQ_ORACLE_STRING_KEY", clazz = String.class)
public class YourEntity {
@TableId(value = "ID_STR", type = IdType.INPUT)
private String idStr;
}自定义ID生成器
如果内置的主键生成策略无法满足需求,可以自定义ID生成器,需要实现IdentifierGenerator接口,并将实现类注入到容器中,主键生成策略配置为ASSIGN_ID或ASSIGN_UUID。
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysUser extends Model<SysUser> {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@TableField
private String name;
private Integer age;
private String email;
}@Component
public class MyIdentifierGenerator implements IdentifierGenerator {
@Override
public Long nextId(Object entity) {
System.out.println("正在生成主键");
return RandomGenerator.getDefault().nextLong();
}
}逻辑删除
逻辑删除是指在表中添加一个标记字段,通过标记字段标识记录是否已被删除。执行查询或更新sql时,where条件带上标记字段,表示数据是否已被删除。
使用方法
- 在applicaiton.yml配置文件中添加相关配置。
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag ## 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 ## 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 ## 逻辑未删除值(默认为 0)- 使用
@TableLogic注解实体类字段,表示该字段为逻辑删除标志字段。
@TableLogic(value = "true", delval = "false")
//如没有配置value或delval则从全局配置中获取。
private Boolean flag;通用枚举
使用@EnumValue注解枚举类的字段,表示该字段为对应到数据库中的字段。
public enum GenderEnum {
MAN(1, "男"), WOMAN(2, "女");
@EnumValue
private final int code;
private final String desc;
GenderEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
}字段类型处理器
类型处理器,用于 JavaType 与 JdbcType 之间的转换,用于 PreparedStatement 设置参数值和从 ResultSet 或 CallableStatement 中取出一个值。
@TableName的autoResultMap属性和@TableField的typeHandler属性配合使用下生效。
自动填充
自动填充功能可以给一些值固定的字段自动填充值,不能每次手动set。比如插入时间、更新时间字段,使用自动填充设置字段值。
使用@TableFiled字段注解类属性,fill属性配置字段的自动填充策略。
FieldFill有以下这些属性:
DEFAULT:默认不处理INSERT:插入时填充字段UPDATE:更新时填充字段INSERT_UPDATE:插入和更新时填充字段。
还需要实现MetaObjectHandler接口,实现其中的insertFill和updateFill方法,并将实现类注入到容器中。
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "operator", String.class, "Jetty");
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "operator", String.class, "Tom");
}
}SQL注入器
执行SQL分析打印
该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长 3.1.0 以上版本
p6spy是一个Java开源工具,用于监控和记录应用程序与数据库之间的交互。它可以通过代理方式嵌入到应用程序和数据库之间的通信中,以便捕获和记录SQL语句、参数和执行时间等信息。p6spy可以帮助开发人员调试和分析数据库访问的性能问题,以及监测和审计应用程序对数据库的操作。
如何使用
- 引入p6spy依赖
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>- 在application.xml配置驱动和数据库url
spring:
datasource:
#开启p6spy的sql分析
username: root
password: test
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:h2:mem:test- 添加spy.properties配置文件。
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
## 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2数据安全保护
MP提供了AES类加解密组件,可以方便地对敏感数据进行加解密。AES是一种对称加密算法。
如何使用
- 通过AES类提供的api获取密钥、加解密数据。
@MybatisPlusTest
public class AESTest {
@Test
public void aesTest() {
String data = "hello world";
//生成随机的AES对称加密密钥
String key = AES.generateRandomKey();
System.out.println("key = " + key);
//加密
String encryptText = AES.encrypt(data, key);
System.out.println("encryptText = " + encryptText);
//解密
String decryptText = AES.decrypt(encryptText, key);
System.out.println("decryptText = " + decryptText);
}
}- 配置文件中,需要加密的数据,前缀增加
mpw:。
// 加密配置 mpw: 开头紧接加密内容( 非数据库配置专用 YML 中其它配置也是可以使用的 )
spring:
datasource:
url: mpw:qRhvCwF4GOqjessEB3G+a5okP+uXXr96wcucn2Pev6Bf1oEMZ1gVpPPhdDmjQqoM
password: mpw:Hzy5iliJbwDHhjLs1L0j6w==
username: mpw:Xb+EgsyuYRXw7U7sBJjBpA==- Jar启动参数添加--mpw.key=对称加密密钥.
// Jar 启动参数( idea 设置 Program arguments , 服务器可以设置为启动环境变量 )
--mpw.key=d1104d7c3b616f0b多数据源
MP提供了两个多数据源的拓展:
- dynamic-datasource:开源,文档付费
- mybatis-mate:企业级付费授权,资料文档免费
dynamic-datasource的使用
- pom引入依赖dynamic-datasource-spring-boot-starter。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>- 配置数据源。
spring:
datasource:
#driver-class-name: org.h2.Driver
#url: jdbc:h2:mem:test
#开启p6spy的sql分析
username: root
password: test
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:h2:mem:test
dynamic:
datasource:
h2:
username: root
password: test
driver-class-name: com.p6spy.engine.spy.P6SpyDriver # 3.2.0开始支持SPI可省略此配置
url: jdbc:p6spy:h2:mem:test
mysql:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://127.0.0.1:3306/test
username: test
password: 123456
primary: h2 #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源- 使用
@DS注解切换数据源。@DS可以注解在方法上或类上,同时存在则采用就近原则,方法上注解优于类上注解。
| 注解 | 结果 |
|---|---|
没有@DS | 默认数据源 |
@DS("dsName") | dsName可以为组名也可以为具体某个库的名称 |
@Mapper
@DS("master")
public interface CountryMapper extends BaseMapper<Country> {
}Mybatisx快速开发插件
MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。
企业高级特性
- 数据审计
- 数据敏感词过滤
- 数据范围(数据权限)
@DataScope、@DataColumn - 表结构自动维护
- 字段数据绑定(字典回写)
@FieldBind - 虚拟属性绑定
@JsonBind - 字段加密解密
@FieldEncrypt - 字段脱敏
@FieldSensitive - 多数据源分库分表(读写分离)
@Sharding - 多数据源动态加载卸载
- 多数据源事务( jta atomikos)
脚本自动维护
null
MybatisPlus插件
MybatisPlusInterceptor核心插件类
MybatisPlusInterceptor是MP的核心插件,继承了mybatis的Interceptor接口,目前代理了 Executor#query 和 Executor#update 和 StatementHandler#prepare 方法。
InnerInterceptor功能插件接口
MP同时也提供了很多插件,这些插件都是基于InnerInterceptor接口实现。
- 自动分页: PaginationInnerInterceptor
- 多租户: TenantLineInnerInterceptor
- 动态表名: DynamicTableNameInnerInterceptor
- 乐观锁: OptimisticLockerInnerInterceptor
- sql 性能规范: IllegalSQLInnerInterceptor
- 防止全表更新与删除: BlockAttackInnerInterceptor
插件使用方式
要引入MP提供的插件,可以使用以下方式配置,以引入分页插件来演示:
@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
}使用拦截忽略注解
@InterceptorIgnore可以实现对某个mapper停用插件功能。
分页插件 PaginationInnerInterceptor
- 核心插件
MybatisPlusInterceptor添加分页插件,并注入到容器中。
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//添加分页插件
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}- 调用带分页参数的api,传入分页对象,使用Page.of(long current, long size)静态方法创建Page对象。
@Test
public void testPage() {
Page<SysUser> sysUserPage = sysUserMapper.selectPage(Page.of(1, 2), null);
List<SysUser> records = sysUserPage.getRecords();
records.forEach(System.out::println);
}分页插件也可以使用PageHelper,引入相关依赖即可。分页插件不要同时使用多个。
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>@Test
public void pageHelperTest() {
PageHelper.startPage(1, 2);
List<SysUser> sysUsers = sysUserMapper.selectList(null);
sysUsers.forEach(System.out::println);
PageInfo<SysUser> pageInfo = PageHelper.startPage(1, 3).doSelectPageInfo(() -> sysUserMapper.selectList(null));
List<SysUser> sysUserList = pageInfo.getList();
sysUserList.forEach(System.out::println);
}乐观锁插件 OptimisticLockerInnerInterceptor
乐观锁是一种并发控制机制,核心思想是假设在大多数情况下并发冲突是罕见的,因此不会阻止多个用户或线程对资源进行操作,而是会在更新数据时检查数据是否存在冲突。如发生冲突,再采取适当的措施。
数据库中乐观锁的实现方式:
- 取出记录,获取当前的version。
- 更新时,where条件带上version。
- 如果version值与查询获取的值相等,则更新成功,并且更新version的值,如:version=version+1。
- 如果version值不对,则更新失败。
MP中提供了OptimisticLockerInnerInterceptor插件类和@Version注解实现乐观锁。使用@Version注解在类字段上,表示字段为version字段并启用乐观锁功能。
- 核心插件
MybatisPlusInterceptor添加分页插件,并注入到容器中。
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//添加乐观锁插件
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}- 在实体类的字段上加上
@Version字段。
@Version
private Integer version;- 乐观锁支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 仅支持
updateById(id)和update(entity,wrapper),wrapper不可以复用version。
- 在updateById(id) 与 update(entity, wrapper) 方法中使用乐观锁。
@Test
public void testOptimisticLockerTest() {
SysUser chenzhuowen = SysUser.builder().id(1L).name("chenzhuowen").age(28).email("512745183@qq.com").version(0).build();
sysUserMapper.updateById(chenzhuowen);
SysUser sysUser = sysUserMapper.selectById(1L);
System.out.println(sysUser.toString());
}多租户插件 TenantLineInnerInterceptor
防全表更新与删除插件 BlockAttackInnerInterceptor
- 核心插件
MybatisPlusInterceptor添加分页插件,并注入到容器中。
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//添加防全表更新与删除插件
mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return mybatisPlusInterceptor;
}- 在update语句中执行全表更新时,会抛出异常表示更新操作被禁止。
@Test
public void testBlockAttackInnerInterceptorTest() {
SysUser chenzhuowen = SysUser.builder().name("chenzhuowen").age(28).build();
//不带条件更新
sysUserMapper.update(chenzhuowen, null);
List<SysUser> sysUsers = sysUserMapper.selectList(null);
sysUsers.forEach(System.out::println);
}Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
### The error may exist in com/chenzhuowen/mybatisplusstudy/mapper/SysUserMapper.java (best guess)
### The error may involve com.chenzhuowen.mybatisplusstudy.mapper.SysUserMapper.update
### The error occurred while executing an update
### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:196)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:427)
... 77 more动态表名插件 DynamicTableNameInnerInterceptor
- 原理为解析替换设定表名为处理器的返回表名,表名建议可以定义复杂一些避免误替换
- 例如:真实表名为 user 设定为 mp_dt_user 处理器替换为 user_2019 等
使用方法
- 核心插件
MybatisPlusInterceptor添加分页插件,并注入到容器中。
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//添加动态表名插件
//实现TableNameHandler接口,定义表名处理器
TableNameHandler tableNameHandler = new TableNameHandler() {
@Override
public String dynamicTableName(String sql, String tableName) {
System.out.println("sql = " + sql + ", tableName = " + tableName);
return tableName;
}
};
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
dynamicTableNameInnerInterceptor.setTableNameHandler(tableNameHandler);
mybatisPlusInterceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
return mybatisPlusInterceptor;
}- 插件会拦截所有sql,并对sql中的tableName进行修改。