MyBatis-Plus——SQL注入器
MyBatis-Plus的SQL注入器其实是一种“扩展工具包”机制——当BaseMapper提供的默认方法(如selectById、deleteById)不够用时,我们可以通过它自定义通用方法,让所有Mapper接口都能直接使用。这里提供一个案例:自定义一个“删除表中所有数据”的deleteAll方法,用“工具箱”的比喻一步步拆解。
一、先理解核心需求:为什么需要自定义deleteAll?
MyBatis-Plus的BaseMapper虽然提供了很多基础方法,但没有“删除全表数据”的deleteAll方法(可能是为了安全,避免误操作)。如果多个表都需要“删除全表”功能,总不能在每个Mapper里重复写SQL吧?
这时候就需要SQL注入器:把deleteAll定义成“全局通用方法”,让所有继承BaseMapper的接口(比如StudentMapper、UserMapper)都能直接调用,不用重复开发。
二、逐步拆解:每个类的作用
1. 自定义方法类(DeleteAll):定义“具体工具”
// 注入方法类:相当于定义一个"删除全表"的工具
public class DeleteAll extends AbstractMethod {@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {// 1. 定义要执行的SQL:delete from 表名(表名通过tableInfo获取,自动适配不同表)String sql = "delete from " + tableInfo.getTableName();// 2. 定义方法名:和Mapper接口中声明的方法名一致(这里是deleteAll)String method = "deleteAll";// 3. 构建SqlSource:MyBatis中用于传递SQL的对象SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, modelClass);// 4. 构建删除类型的MappedStatement(MyBatis执行SQL的核心对象)return this.addDeleteMappedStatement(mapperClass, method, sqlSource);}
}
作用:这是“具体工具的设计图”,定义了deleteAll方法要执行的SQL(删除全表)、方法名,以及如何生成MyBatis能识别的执行对象(MappedStatement)。
继承AbstractMethod的原因:MyBatis-Plus通过AbstractMethod统一管理方法生成逻辑,我们只需按照它的规范实现即可。
2. SQL注入器(MySqlInject):将“工具”放入“公共工具箱”
// SQL自动注入器:相当于把自定义工具放到所有Mapper都能访问的公共工具箱
@Component
public class MySqlInject extends AbstractSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {List<AbstractMethod> methods = new ArrayList<>();// 把自定义的DeleteAll方法添加到方法列表中methods.add(new DeleteAll()); return methods;}
}
作用:注入器的职责是“收集所有自定义方法”,并在MyBatis-Plus启动时,将这些方法注入到所有继承BaseMapper的接口中。
简单说:没有注入器,DeleteAll方法只是一个孤立的类;有了注入器,它会告诉MyBatis-Plus:“把deleteAll方法加到所有Mapper里,让它们都能用”。
3. 在Mapper接口中声明方法:“告诉工具箱要用这个工具”
// StudentMapper:具体的工具箱,继承BaseMapper后,自动获得基础工具+注入的自定义工具
public interface StudentMapper extends BaseMapper<Student> {// 声明deleteAll方法(无需实现,因为注入器已经帮我们生成了实现)void deleteAll();
}
为什么只声明不实现?
因为DeleteAll类已经通过注入器生成了对应的SQL和执行逻辑,这里只是“告诉MyBatis:我要用这个方法”,实际执行时会找到注入器生成的实现。
4. 注销BlockAttackInnerInterceptor插件:“暂时关闭安全锁”
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));// 注释掉防全表删除插件:因为deleteAll就是全表删除,会被它拦截// interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;
}
原因:BlockAttackInnerInterceptor是MyBatis-Plus的“安全插件”,会拦截全表删除/更新(防止误操作)。而我们的deleteAll就是要全表删除,所以需要暂时注释掉,否则会执行失败。
5. 测试方法:“使用工具”
@Test
public void testDeleteAll() {// 直接调用StudentMapper中的deleteAll方法studentMapper.deleteAll();
}
此时调用的deleteAll,就是我们通过注入器自定义的全表删除方法。
三、完整执行流程:从启动到调用
用“工具箱准备→使用工具”的流程理解:
-
启动阶段:准备工具箱
- 项目启动时,Spring会加载MySqlInject(因为加了@Component);
- MySqlInject的getMethodList方法被调用,返回包含DeleteAll的方法列表;
- MyBatis-Plus会根据这些方法,为所有继承BaseMapper的接口(如StudentMapper)生成对应的实现逻辑(即MappedStatement,包含SQL和执行方式);
- 最终,StudentMapper不仅有BaseMapper的默认方法,还多了deleteAll方法。
-
调用阶段:使用工具
- 执行studentMapper.deleteAll()时,MyBatis会找到注入器生成的MappedStatement;
- 执行其中定义的SQL:
delete from student
(表名由TableInfo自动获取,对应Student实体类的表); - 数据库执行全表删除,完成操作。
四、总结
SQL注入器就像“全局工具注册中心”:
- 自定义方法类(DeleteAll)是“工具的设计图”,定义了工具的功能(执行什么SQL);
- 注入器(MySqlInject)是“工具管理员”,把工具分发到所有Mapper的“工具箱”里;
- Mapper接口声明方法是“告诉管理员要用这个工具”;
- 最终,我们可以在任何Mapper中直接使用这个工具(调用方法)。
这种机制的好处是:一次定义,到处使用,避免重复开发通用方法。