大纲 前言 版本说明 本文的教程内容是基于 MyBatis-Plus 3.5.2
版本编写的,若你使用的是 2.x
或其他版本,可能会有部分知识点、案例代码不兼容,一切以 MyBatis-Plus 官方文档为准。
MyBatis-Plus 插件介绍 InnerInterceptor 接口 MyBatis-Plus 的插件都是基于 InnerInterceptor
接口来实现的,目前已有的插件:
分页 - PaginationInnerInterceptor
多租户 - TenantLineInnerInterceptor
动态表名 - DynamicTableNameInnerInterceptor
乐观锁 - OptimisticLockerInnerInterceptor
SQL 性能规范 - IllegalSQLInnerInterceptor
防止全表更新与删除 - BlockAttackInnerInterceptor
使用多个插件时需要注意顺序关系,建议使用如下顺序
多租户、动态表名 分页、乐观锁 SQL 性能规范、防止全表更新与删除 总结:对 SQL 进行单次改造的优先放入,不对 SQL 进行改造的最后放入 忽略插件拦截的注解 @InterceptorIgnore
注解可用于忽略插件的拦截,支持注解在 Mapper 上以及 Mapper 方法上,若两者同时存在则 Mapper 方法 比 Mapper 的优先级高。各属性表示对应的插件,支持取值为 true
和 false
、1
和 0
、on
和 off
。属性值返回 true
表示不走插件(在配置了插件的情况下,不填则默认表示 false
)。属性列表如下:
属性名 类型 默认值 描述 tenantLine String “” 行级租户 dynamicTableName String “” 动态表名 blockAttack String “” 攻击 SQL 阻断解析器,防止全表更新与删除 illegalSql String “” 垃圾 SQL 拦截
更多的使用说明详见 @InterceptorIgnore
注解的源码注释
MyBatis-Plus 插件使用 本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-plus-lesson-07
。
MyBatis-Plus 分页插件 分页插件的介绍 PaginationInnerInterceptor
分页拦截器类的属性说明如下:
属性名 类型 默认值 描述 overflow boolean false 溢出总页数后是否进行处理 (默认不处理,参见 插件#continuePage
方法) maxLimit Long 单页分页条数限制 (默认无限制,参见 插件#handlerLimit
方法) dbType DbType 数据库类型 (根据类型获取应使用的分页方言,参见 插件#findIDialect
方法) dialect IDialect 方言实现类 (参见 插件#findIDialect
方法)
注意
建议单一数据库类型的均设置 dbType
属性,避免每次分页都去抓取数据库类型 生成 countSql
会在 left join
的表不参与 where
条件的情况下,把 left join
优化掉 所以建议任何带有 left join
的 SQL,都写成标准 SQL,即给于表一个别名,字段也要 别名.字段
分页插件的使用 分页插件的配置步骤
只需要使用 Spring XML 方式、 SpringBoot 配置类方式或者 MyBatis 配置文件方式中的任意一种配置乐观锁插件即可。
配置分页插件 注入 MybatisPlusInterceptor
类,并配置 PaginationInnerInterceptor
拦截器。
Spring XML 方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="plugins" > <array > <ref bean ="mybatisPlusInterceptor" /> </array > </property > </bean > <bean id ="mybatisPlusInterceptor" class ="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor" > <property name ="interceptors" > <list > <ref bean ="paginationInnerInterceptor" /> </list > </property > </bean > <bean id ="paginationInnerInterceptor" class ="com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor" > <constructor-arg name ="dbType" value ="MYSQL" /> </bean >
SpringBoot 配置类方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Configuration @MapperScan("com.clay.mybatis.dao") public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
若 MyBatis-Plus 使用的是较低的版本(例如 3.3.1
),则配置类的写法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Configuration @MapperScan("com.clay.mybatis.dao") public class MyBatisPlusConfiguration { @Bean public PaginationInterceptor paginationInterceptor () { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); paginationInterceptor.setDbType(DbType.MYSQL); return paginationInterceptor; } }
提示
特殊情况下,可能还需要在启动类上通过 @ComponentScan
注解来扫描上面定义的 MybatisPlusConfig
配置类。
1 2 3 4 5 6 7 8 9 @SpringBootApplication @ComponentScan("com.clay.mybatis") public class MyBatisPlusApplication { public static void main (String[] args) { SpringApplication.run(MyBatisPlusApplication.class, args); } }
MyBatis 配置文件方式 1 2 3 4 5 6 7 8 9 <configuration > <plugins > <plugin interceptor ="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor" > <property name ="@page" value ="com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor" /> <property name ="page:dbType" value ="mysql" /> </plugin > </plugins > </configuration >
分页操作(基于 AR 模式) MyBatis-Plus 分页插件支持在 ActiveRecord 模式下使用,示例代码如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Employee extends Model <Employee > { private Long id; private String lastName; private String gender; private String email; private Integer age; .... @Override public Serializable pkVal () { return this .id; } }
1 2 3 public interface EmployeeMapper extends BaseMapper <Employee > {}
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 @SpringBootTest public class PagePluginTest { @Test public void selectByPageForAR () { Page<Employee> page = new Page<>(2 , 2 ); QueryWrapper<Employee> wrapper = new QueryWrapper<>(); wrapper.eq("gender" , "1" ); Employee employee = new Employee(); employee.selectPage(page, wrapper); List<Employee> list = page.getRecords(); list.forEach(System.out::println); System.out.println("总页数: " + page.getPages()); System.out.println("总记录数: " + page.getTotal()); System.out.println("当前的页码: " + page.getCurrent()); System.out.println("每页的记录数: " + page.getSize()); System.out.println("是否有上一页: " + page.hasPrevious()); System.out.println("是否有下一页: " + page.hasNext()); } }
执行上面的测试代码后,控制台输出的日志信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ==> Preparing: SELECT COUNT(*) AS total FROM t_employee WHERE (gender = ?) ==> Parameters: 1(String) <== Columns: total <== Row: 4 <== Total: 1 ==> Preparing: SELECT id,email,last_name,gender,age FROM t_employee WHERE (gender = ?) LIMIT ?,? ==> Parameters: 1(String), 2(Long), 2(Long) <== Columns: id, email, last_name, gender, age <== Row: 3, jim@gmail.com, Jim, 1, 26 <== Row: 4, peter@gmail.com, Peter, 1, 29 <== Total: 2 Employee [id=3, lastName=Jim, gender=1, email=jim@gmail.com, age=26] Employee [id=4, lastName=Peter, gender=1, email=peter@gmail.com, age=29] 总页数: 2 总记录数: 4 当前的页码: 2 每页的记录数: 2 是否有上一页: true 是否有下一页: false
分页操作(基于通用 Mapper) MyBatis-Plus 分页插件支持通用 Mapper 的使用,示例代码如下。
1 2 3 public interface EmployeeMapper extends BaseMapper <Employee > {}
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 @SpringBootTest public class PagePluginTest { @Autowired private EmployeeMapper empMapper; @Test public void selectByPage () { Page<Employee> page = new Page<>(2 , 2 ); QueryWrapper<Employee> wrapper = new QueryWrapper<>(); wrapper.eq("gender" , "1" ); empMapper.selectPage(page, wrapper); List<Employee> list = page.getRecords(); list.forEach(System.out::println); System.out.println("总页数: " + page.getPages()); System.out.println("总记录数: " + page.getTotal()); System.out.println("当前的页码: " + page.getCurrent()); System.out.println("每页的记录数: " + page.getSize()); System.out.println("是否有上一页: " + page.hasPrevious()); System.out.println("是否有下一页: " + page.hasNext()); } }
分页操作(基于自定义 Mapper 方法) MyBatis-Plus 分页插件支持自定义 Mapper 方法的分页查询,示例代码如下。
提示
1、自定义 Mapper 方法时,需要指定返回值的类型为 IPage
或 List
,同时还需要指定其中的一个方法参数的类型为 IPage
2、若自定义 Mapper 方法的返回类型是 IPage
,则入参的 IPage
不能为 null
,因为返回的 IPage
等于入参的 IPage
;如果想临时不分页,可以在初始化 IPage
时指定 size
属性为小于零的值即可 3、若自定义 Mapper 方法的返回类型是 List
,则入参的 IPage
可以为 null
(为 null
则表示不分页) 4、若在 SQL 映射文件中,需要从 page
对象里取值,可以使用 page.属性名
的方式来获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public interface EmployeeMapper extends BaseMapper <Employee > { IPage<Employee> queryByPage (IPage<Employee> page, @Param("gender") String gender) ; List<Employee> queryListByPage (IPage<Employee> page, @Param("gender") String gender) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <mapper namespace ="com.clay.mybatis.dao.EmployeeMapper" > <select id ="queryByPage" resultType ="Employee" > select id, email, last_name as lastName, gender, age from t_employee where gender = #{gender} </select > <select id ="queryListByPage" resultType ="Employee" > select id, email, last_name as lastName, gender, age from t_employee where gender = #{gender} </select > </mapper >
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 @SpringBootTest public class PagePluginTest { @Autowired private EmployeeMapper empMapper; @Test public void queryByPage () { Page<Employee> page = new Page<>(2 , 2 ); empMapper.queryByPage(page, "1" ); List<Employee> list = page.getRecords(); list.forEach(System.out::println); System.out.println("总页数: " + page.getPages()); System.out.println("总记录数: " + page.getTotal()); System.out.println("当前的页码: " + page.getCurrent()); System.out.println("每页的记录数: " + page.getSize()); System.out.println("是否有上一页: " + page.hasPrevious()); System.out.println("是否有下一页: " + page.hasNext()); } @Test public void queryListByPage () { Page<Employee> page = new Page<>(2 , 2 ); List<Employee> list = empMapper.queryListByPage(page, "1" ); list.forEach(System.out::println); System.out.println("总页数: " + page.getPages()); System.out.println("总记录数: " + page.getTotal()); System.out.println("当前的页码: " + page.getCurrent()); System.out.println("每页的记录数: " + page.getSize()); System.out.println("是否有上一页: " + page.hasPrevious()); System.out.println("是否有下一页: " + page.hasNext()); } }
MyBatis-Plus 乐观锁插件 本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-plus-lesson-08
。
真实业务场景 一件商品,成本价是 80 元,售价是 100 元。老板先是通知小李,说你去把商品价格增加 50 元。小李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到 150 元,价格太高,可能会影响销量。又通知小王,你把商品价格降低 30 元。此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格 100 元;小王也在操作,取出的商品价格也是 100 元。小李将价格加了 50 元,并将 100 + 50 = 150 元存入了数据库;小王将商品减了 30 元,并将 100 - 30 = 70 元存入了数据库。是的,如果没有锁,小李的操作就完全被小王的覆盖了。现在商品价格是 70 元,比成本价低 10 元。几分钟后,这个商品很快出售了 1 千多件商品,老板亏了 1 万多元。如果这里使用乐观锁,在小王保存价格前,可以检查到价格是否被人修改过。如果价格被修改过,则重新取出被修改后的价格(150 元),这样他最终会将 120 元存入数据库。如果是使用悲观锁,在小李取出数据时,小王只能等小李完成价格的更改之后,才能对价格进行操作,这也会保证最终的价格是 120 元。
模拟数据更改冲突 1 2 3 4 5 6 7 8 9 10 11 12 CREATE TABLE `t_product`( id BIGINT (20 ) NOT NULL AUTO_INCREMENT COMMENT '主键ID' , name VARCHAR (50 ) DEFAULT NULL COMMENT '商品名称' , price DECIMAL (10 , 5 ) DEFAULT 0 COMMENT '价格' , version BIGINT (20 ) DEFAULT 0 COMMENT '乐观锁版本号' , PRIMARY KEY (id) ) ENGINE= InnoDB DEFAULT CHARSET= utf8; insert into t_product (id, name, price) values (1 , 'C++ Primer Plus' , 100 );
1 2 3 4 5 6 7 8 9 10 public class Product { private Long id; private String name; private Long version; private BigDecimal price; ... }
1 2 3 public interface ProductMapper extends BaseMapper <Product > {}
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 @SpringBootTest public class MyBatisPlusApplicationTest { @Autowired private ProductMapper productMapper; @Test public void updatePrice () { Product product1 = productMapper.selectById(1L ); System.out.println("小李取出的商品价格:" + product1.getPrice()); Product product2 = productMapper.selectById(1L ); System.out.println("小王取出的商品价格:" + product2.getPrice()); product1.setPrice(product1.getPrice().add(new BigDecimal(50 ))); int result1 = productMapper.updateById(product1); System.out.println("小李修改商品价格的结果: " + (result1 > 0 )); product2.setPrice(product2.getPrice().subtract(new BigDecimal(30 ))); int result2 = productMapper.updateById(product2); System.out.println("小王修改商品价格的结果: " + (result2 > 0 )); Product product3 = productMapper.selectById(1L ); System.out.println("最终的商品价格: " + product3.getPrice()); } }
执行上面的测试代码后,控制台输出的日志信息如下,观察后可发现最终的商品价格并不是预期的 120 元。
1 2 3 4 5 小李取出的商品价格:100.00000 小王取出的商品价格:100.00000 小李修改商品价格的结果: true 小王修改商品价格的结果: true 最终的商品价格: 70.00000
乐观锁的实现流程 1 2 3 4 5 6 7 8 CREATE TABLE `t_product`( id BIGINT (20 ) NOT NULL AUTO_INCREMENT COMMENT '主键ID' , name VARCHAR (50 ) DEFAULT NULL COMMENT '商品名称' , price DECIMAL (10 , 5 ) DEFAULT 0 COMMENT '价格' , version BIGINT (20 ) DEFAULT 0 COMMENT '乐观锁版本号' , PRIMARY KEY (id) ) ENGINE= InnoDB DEFAULT CHARSET= utf8;
第二步:读取数据库表记录时,获取当前记录的 version
(oldVersion) 1 select id, name, price, version from t_product where id = 1 ;
第三步:更新数据库表记录时,执行 version + 1
,如果 where
语句中的 version
版本不满足条件,则更新失败 1 update t_product set price = price + 50 , version = version + 1 where id = 1 and version = oldVersion;
或者可以简单理解为以下的 SQL 语句
1 update t_product set price = newPrice, version = newVersion where id = 1 and version = oldVersion;
乐观锁插件的使用 乐观锁插件的配置步骤
第一步
:使用 Spring XML 方式、 SpringBoot 配置类方式或者 MyBatis 配置文件方式中的任意一种配置乐观锁插件第二步
:在实体类的属性上添加 @Version
注解配置乐观锁插件 注入 MybatisPlusInterceptor
类,并配置 OptimisticLockerInnerInterceptor
拦截器。
Spring XML 方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="plugins" > <array > <ref bean ="mybatisPlusInterceptor" /> </array > </property > </bean > <bean id ="mybatisPlusInterceptor" class ="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor" > <property name ="interceptors" > <list > <ref bean ="optimisticLockerInnerInterceptor" /> </list > </property > </bean > <bean id ="optimisticLockerInnerInterceptor" class ="com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor" />
SpringBoot 配置类方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Configuration @MapperScan("com.clay.mybatis.dao") public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }
提示
特殊情况下,可能还需要在启动类上通过 @ComponentScan
注解来扫描上面定义的 MybatisPlusConfig
配置类。
1 2 3 4 5 6 7 8 9 @SpringBootApplication @ComponentScan("com.clay.mybatis") public class MyBatisPlusApplication { public static void main (String[] args) { SpringApplication.run(MyBatisPlusApplication.class, args); } }
MyBatis 配置文件方式 1 2 3 4 5 6 7 8 <configuration > <plugins > <plugin interceptor ="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor" > <property name ="@optimisticLocker" value ="com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor" /> </plugin > </plugins > </configuration >
添加 @Version 注解 在实体类的属性上添加 @Version
注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Product { private Long id; private String name; @Version private Long version; private BigDecimal price; ... }
特别注意
@Version
注解支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime整数类型下 newVersion = oldVersion + 1
,其中的 newVersion
会回写到 Entity 中 @Version
注解仅支持 updateById(id)
与 update(entity, wrapper)
方法在使用 update(entity, wrapper)
方法的情况下,wrapper
不能复用! Junit 单元测试代码 1 2 3 public interface ProductMapper extends BaseMapper <Product > {}
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 @SpringBootTest public class MyBatisPlusApplicationTest { @Autowired private ProductMapper productMapper; @Test public void updatePrice () { Product product1 = productMapper.selectById(1L ); System.out.println("小李取出的商品价格:" + product1.getPrice()); Product product2 = productMapper.selectById(1L ); System.out.println("小王取出的商品价格:" + product2.getPrice()); product1.setPrice(product1.getPrice().add(new BigDecimal(50 ))); int result1 = productMapper.updateById(product1); System.out.println("小李修改商品价格的结果: " + (result1 > 0 )); product2.setPrice(product2.getPrice().subtract(new BigDecimal(30 ))); int result2 = productMapper.updateById(product2); System.out.println("小王修改商品价格的结果: " + (result2 > 0 )); Product product3 = productMapper.selectById(1L ); System.out.println("最终的商品价格: " + product3.getPrice()); } }
执行上面的测试代码后,控制台输出的日志信息如下,观察后可发现最终只有小李成功更改了商品价格,也就是商品价格的更改不会再发生冲突。
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 ==> Preparing: SELECT id,name,price,version FROM t_product WHERE id=? ==> Parameters: 1(Long) <== Columns: id, name, price, version <== Row: 1, C++ Primer Plus, 100.00000, 0 <== Total: 1 小李取出的商品价格:100.00000 ==> Preparing: SELECT id,name,price,version FROM t_product WHERE id=? ==> Parameters: 1(Long) <== Columns: id, name, price, version <== Row: 1, C++ Primer Plus, 100.00000, 0 <== Total: 1 小王取出的商品价格:100.00000 ==> Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=? ==> Parameters: C++ Primer Plus(String), 150.00000(BigDecimal), 1(Long), 1(Long), 0(Long) <== Updates: 1 小李修改商品价格的结果: true ==> Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=? ==> Parameters: C++ Primer Plus(String), 70.00000(BigDecimal), 1(Long), 1(Long), 0(Long) <== Updates: 0 小王修改商品价格的结果: false 最终的商品价格: 150.00000
优化测试代码
上面的测试代码可以加入更新重试机制,也就是在当前记录更新失败时,可以重新读取记录,然后再次尝试更新记录,示例代码如下:
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 @SpringBootTest public class MyBatisPlusApplicationTest { @Autowired private ProductMapper productMapper; @Test public void updatePrice () { Product product1 = productMapper.selectById(1L ); System.out.println("小李取出的商品价格:" + product1.getPrice()); Product product2 = productMapper.selectById(1L ); System.out.println("小王取出的商品价格:" + product2.getPrice()); product1.setPrice(product1.getPrice().add(new BigDecimal(50 ))); int result1 = productMapper.updateById(product1); System.out.println("小李修改商品价格的结果: " + (result1 > 0 )); product2.setPrice(product2.getPrice().subtract(new BigDecimal(30 ))); int result2 = productMapper.updateById(product2); System.out.println("小王修改商品价格的结果: " + (result2 > 0 )); if (result2 == 0 ) { Product productNew = productMapper.selectById(1L ); productNew.setPrice(productNew.getPrice().subtract(new BigDecimal(30 ))); int resultNew = productMapper.updateById(productNew); System.out.println("小王第二次修改商品价格的结果: " + (resultNew > 0 )); } Product product3 = productMapper.selectById(1L ); System.out.println("最终的商品价格: " + product3.getPrice()); } }
MyBatis-Plus 防全表更新与删除插件 BlockAttackInnerInterceptor
拦截器作用于 update
和 delete
的 SQL 语句,可以阻止恶意的全表更新和全表删除操作。本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-plus-lesson-09
。
防全表更新与删除插件的使用 防全表更新与删除插件的配置步骤
只需要使用 Spring XML 方式、 SpringBoot 配置类方式或者 MyBatis 配置文件方式中的任意一种配置防全表更新与删除插件即可。
配置防全表更新与删除插件 注入 MybatisPlusInterceptor
类,并配置 BlockAttackInnerInterceptor
拦截器。
Spring XML 方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="plugins" > <array > <ref bean ="mybatisPlusInterceptor" /> </array > </property > </bean > <bean id ="mybatisPlusInterceptor" class ="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor" > <property name ="interceptors" > <list > <ref bean ="blockAttackInnerInterceptor" /> </list > </property > </bean > <bean id ="blockAttackInnerInterceptor" class ="com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor" />
SpringBoot 配置类方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Configuration @MapperScan("com.clay.mybatis.dao") public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); return interceptor; } }
提示
特殊情况下,可能还需要在启动类上通过 @ComponentScan
注解来扫描上面定义的 MybatisPlusConfig
配置类。
1 2 3 4 5 6 7 8 9 @SpringBootApplication @ComponentScan("com.clay.mybatis") public class MyBatisPlusApplication { public static void main (String[] args) { SpringApplication.run(MyBatisPlusApplication.class, args); } }
MyBatis 配置文件方式 1 2 3 4 5 6 7 8 <configuration > <plugins > <plugin interceptor ="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor" > <property name ="@blockAttack" value ="com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor" /> </plugin > </plugins > </configuration >
Junit 单元测试代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @SpringBootTest public class MyBatisPlusApplicationTest { @Autowired private EmployeeMapper empMapper; @Test public void deleteAll () { empMapper.delete(null ); } @Test public void updateAll () { Employee employee = new Employee(); employee.setGender("1" ); employee.setAge(26 ); empMapper.update(employee, null ); } }
执行上面的测试代码后,若抛出如下的异常信息,则说明防全表更新与删除插件生效了
1 2 3 4 5 6 7 8 9 10 11 12 13 Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of full table deletion at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:49) at com.baomidou.mybatisplus.core.toolkit.Assert.isTrue(Assert.java:38) at com.baomidou.mybatisplus.core.toolkit.Assert.isFalse(Assert.java:50) at com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor.checkWhere(BlockAttackInnerInterceptor.java:74) at com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor.processDelete(BlockAttackInnerInterceptor.java:65) Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:49) at com.baomidou.mybatisplus.core.toolkit.Assert.isTrue(Assert.java:38) at com.baomidou.mybatisplus.core.toolkit.Assert.isFalse(Assert.java:50) at com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor.checkWhere(BlockAttackInnerInterceptor.java:74) at com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor.processUpdate(BlockAttackInnerInterceptor.java:70)