MyBatis-Plus SQL 注入器是什么?一篇文章带你彻底搞懂!
前言
第一次看到 SQL 注入器(SQL Injector) 这个名字的时候,我直接懵了。
心里第一反应就是:
SQL 注入?不是黑客攻击吗?
后来才发现,MyBatis-Plus 的 SQL 注入器和 SQL 注入攻击没有半毛钱关系!
很多人学 MyBatis-Plus 时,一看到官方文档里的 ISqlInjector、DefaultSqlInjector、AbstractMethod,就开始照着敲代码。
结果代码敲完了,还是不知道:
- SQL 注入器到底是什么?
- 为什么要有 SQL 注入器?
- 什么时候才会用到它?
- 为什么平时开发几乎见不到它?
今天这篇文章,就带大家彻底搞懂 MyBatis-Plus SQL 注入器。
一、先说结论
SQL 注入器只有一句话:
它负责给 Mapper 批量添加通用 SQL 方法。
注意几个关键词:
- 批量
- 通用
- SQL 方法
千万不要理解成:
动态生成 SQL。
它真正做的是:
Mapper
↓
SQL Injector
↓
注册 SQL 方法
↓
Mapper 可以直接调用
二、先看看没有 MyBatis-Plus 的时代
以前 MyBatis 是这样写的。
Mapper:
public interface UserMapper {
User selectById(Long id);
}
然后 XML:
<select id="selectById">
SELECT *
FROM user
WHERE id = #{id}
</select>
每一个方法:
都需要:
Mapper
+
XML
全部自己写。
后来 MyBatis-Plus 出现了。
你突然发现:
userMapper.selectById(1L);
userMapper.insert(user);
userMapper.deleteById(1L);
userMapper.updateById(user);
这些方法:
居然不用写 XML!
很多人第一次都会问:
这些 SQL 到底是谁写的?
答案就是:
MyBatis-Plus 自己帮你注入进去的。
例如:
userMapper.selectById(id);
底层其实执行:
SELECT *
FROM user
WHERE id = ?
但是:
你根本没有写 XML。
为什么?
因为:
BaseMapper
│
▼
SQL Injector
│
▼
注册 SQL
项目启动的时候:
MyBatis-Plus 已经偷偷把这些 SQL 注册好了。
三、为什么叫"SQL 注入器"?
这里很多人都会误会。
SQL 注入器:
不是:
SQL Injection Attack
而是:
Inject SQL
什么意思?
就是:
把 SQL 方法注入到 Mapper 中。
例如:
你定义:
public interface UserMapper
extends BaseMapper<UserDO>{
}
你并没有写:
selectById()
insert()
deleteById()
但是:
启动以后:
实际上:
UserMapper
↓
selectById()
insert()
delete()
update()
selectList()
...
全部都有了。
这些:
就是 SQL Injector 注进去的。
所以:
这里:
注入
不是:
攻击。
而是:
注册。
四、为什么需要 SQL 注入器?
先来看一个例子。
假设公司有:
User
Order
Article
Comment
Category
Product
六张表。
领导突然说:
以后所有表:
都要支持:
deleteAll();
例如:
userMapper.deleteAll();
orderMapper.deleteAll();
articleMapper.deleteAll();
怎么办?
最普通的方法:
每个 Mapper:
写:
int deleteAll();
然后:
每个 XML:
写:
DELETE FROM xxx
例如:
User:
DELETE FROM user
Order:
DELETE FROM order
Article:
DELETE FROM article
......
是不是发现:
除了:
表名不同
其他:
全部一样。
这就是:
重复代码。
五、SQL 注入器就是解决重复 SQL
于是:
我们写:
一个:
DeleteAll
告诉 MyBatis-Plus:
以后:
凡是:
Mapper。
都自动拥有:
deleteAll();
底层:
根据当前 Mapper 对应的表:
自动生成:
例如:
User:
DELETE FROM user;
Order:
DELETE FROM order;
Article:
DELETE FROM article;
是不是:
一下子舒服了。
这就是:
SQL 注入器最大的作用:
把重复 SQL 抽出来,一次编写,多处使用。
六、SQL 注入器什么时候才会用?
这是很多人最关心的问题。
答案:
绝大多数项目,你根本不会写 SQL 注入器。
因为:
MyBatis-Plus 已经帮你注入好了:
insert
deleteById
updateById
selectById
selectList
selectPage
这些:
已经够用了。
所以:
很多人:
用了三四年:
一次:
SQL 注入器:
都没写过。
七、什么时候需要自己写?
通常只有一种情况:
很多 Mapper 都需要同一个 SQL。
例如:
公司统一要求:
所有 Mapper:
都有:
insertBatch();
批量插入。
或者:
updateAllById();
批量更新。
或者:
physicalDeleteById();
物理删除。
这些:
所有 Mapper:
都需要。
如果:
每个 Mapper:
自己写。
每个 XML:
自己写。
维护成本:
非常高。
于是:
写一个:
SQL 注入器。
全部:
自动拥有。
八、什么时候不用 SQL 注入器?
例如:
只有:
UserMapper
需要:
selectVipUser();
那么:
直接:
Mapper:
List<User> selectVipUser();
XML:
<select>
SELECT *
FROM user
WHERE level='VIP'
</select>
结束。
根本:
不用:
SQL 注入器。
因为:
只有:
一个 Mapper:
会用。
九、SQL 注入器到底干了什么?
可以简单理解成:
项目启动的时候:
MyBatis-Plus:
开始扫描:
UserMapper
OrderMapper
ArticleMapper
发现:
UserMapper。
对应:
user
于是:
自动:
注册:
DELETE FROM user
发现:
OrderMapper。
对应:
order
于是:
注册:
DELETE FROM order
最后:
MyBatis:
里面:
多了一堆:
MappedStatement。
以后:
你调用:
userMapper.deleteAll();
其实:
就是执行:
对应:
DELETE FROM user;
整个过程:
可以画成:
Spring Boot 启动
│
▼
扫描 Mapper
│
▼
读取实体信息
│
▼
SQL Injector
│
▼
生成 SQL
│
▼
注册到 MyBatis
│
▼
Mapper 可以直接调用
十、SQL 注入器源码主要有三个角色
官方源码里面:
主要有三个类。
① BaseMapper
定义:
有哪些方法。
例如:
insert();
selectById();
updateById();
或者:
你新增:
deleteAll();
这里只是:
告诉 Java:
有这个方法。
但是:
没有 SQL。
② AbstractMethod
负责:
描述:
这个方法:
执行什么 SQL。
例如:
deleteAll()
↓
DELETE FROM 表名
以后:
无论:
User。
还是:
Order。
都是:
这里生成。
③ DefaultSqlInjector
负责:
把:
这些:
SQL 方法:
注册:
到:
MyBatis。
最终:
Mapper:
才能:
真正执行。
所以:
三者关系:
BaseMapper
定义方法
│
▼
AbstractMethod
定义 SQL
│
▼
DefaultSqlInjector
注册 SQL
│
▼
Mapper 可以使用
十一、它和 MapStruct 有没有点像?
如果你刚学完:
MapStruct。
其实会发现:
它们思想:
非常像。
MapStruct:
把:
setName()
setAge()
setAvatar()
这些:
重复:
对象转换:
抽出来。
变成:
UserConvert.convert();
SQL 注入器:
把:
DELETE
UPDATE
INSERT
这些:
重复:
SQL:
抽出来。
变成:
统一:
注册。
是不是:
本质:
都是:
发现重复
↓
抽象
↓
统一管理
↓
统一复用
这其实就是:
软件设计里面:
非常经典的一种思想:
不要让重复代码到处出现,而是把公共能力集中起来。
十二、一个非常重要的误区
很多人学完 SQL 注入器。
以后:
所有 SQL:
都想:
写:
SQL 注入器。
其实:
完全没必要。
真正开发:
应该这样选择:
普通 CRUD:
BaseMapper
单个 Mapper 特殊查询:
Mapper + XML
很多 Mapper 共用同一种 SQL:
SQL Injector
记住:
SQL 注入器:
不是:
替代 XML。
而是:
扩展 BaseMapper。
总结
SQL 注入器并没有名字听起来那么神秘。
它的本质,其实就是:
把一套通用 SQL 方法,在项目启动时统一注册到所有 Mapper 中,让不同 Mapper 可以共享这些 SQL 能力。
所以它适合解决的是:
很多 Mapper
都有同一个 SQL
↓
重复代码
↓
统一抽象
↓
统一注册
↓
全部复用
最后,记住一句话就够了:
SQL 注入器不是用来写业务 SQL 的,而是用来扩展 MyBatis-Plus 通用 CRUD 能力的。
当一个 SQL 只有某一个 Mapper 会使用时,直接写 Mapper + XML 即可;当一个 SQL 希望所有 Mapper 都拥有时,SQL 注入器才是真正适合的解决方案。