首先看一下 Mybatis 的查询过程:
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1896ae05] was not registered for synchronization because synchronization is not active
==> Parameters: 1459(String)
2021-09-18 06:35:20.413 DEBUG 6 --- [nio-8080-exec-1] o.s.jdbc.datasource.DataSourceUtils : Fetching JDBC Connection from DataSource
JDBC Connection [com.mysql.jdbc.JDBC4Connection@14aeb8db] will not be managed by Spring
==> Preparing: select map_link from map_info where map_id=? limit 1;
<== Total: 0
==> Parameters: 1458(String)
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@500b701f]
可以发现, Mybatis 先是开启了一次 SqlSession 查询,然后进行 Preparing,这个过程其实对应的就是 PreparedStatement,它做了一件核心的事情:将 SQL 执行语句和参数进行分离了。
所谓分离,指的是 sql 执行语句和传入的参数是分开的,参数用占位符
?
来代替。
接下来看一下 MySql 一次查询的原理过程(《高性能 MYSQL》)
MySQL执行一个查询的过程如图6-1所示:
- 在通信协议的支持下,客户端向MySQL服务器发送一条查询语句
- 服务器先检查查询缓存,如果命中了缓存,则立刻返回存储在缓存中的结果
- 否则,服务器端进行SQL解析、预处理,再由优化器生成对应的执行计划
- MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询
- 将结果返回给客户端
看一下 StackOverFlow 上的回答:
While in case of prepared statements we don’t alter our program, it remains intact
假设对于下列语句
$spoiled_data = "1; DROP TABLE users;"
$query = "SELECT * FROM users where id=$spoiled_data";
其防注入的过程如下:
-
向 MySQL 发送一条查询语句,数据会被占位符的
?
的变量替换 。注意,该条查询被发送到服务器,其中是没有任何数据$db->prepare("SELECT * FROM users where id=?");
-
接着发送数据到 MySQL 服务器上。也就是这条命令,MySQL只会把他当成数据放到占位符上,里面即使有 SQL 命令并不会去执行,于是便防止了注入
$db->execute($data);