PreparedStatement与Statement
1、PreparedStatement效率为什么高
PreparedStatement会讲SQL语句进行预编译,每次执行的时候只需要将参数设置给相应的占位符就可以运行。而使用Statement时,SQL语句时每次都要进行编译,所以PreparedStatement的效率相对较高。
1.1、具体分析
SQL在DB中的执行步骤
- 将SQL 语句转化为DB形式(语法树结构)
- 检查语法
- 检查语义
- 准备执行计划(也是优化的过程,这个步骤比较重要,关系到你SQL文的效率)
- 设置运行时的参数
- 执行查询并取得结果
所谓的==预编译==就是同样的SQL【包括不同参数的】,1-4步骤只在第一次执行,所以大大提高了执行效率。
2、PreparedStatement如何防止SQL注入
使用Statement时,如果SQL当中出现了
'或者-等符号时,需要使用转义字符来进行转义,而在PreparedStatement当中,如果占位符的值当中有这些符号,PreparedStatement会自动的进行转义。
- 不使用PreparedStatement的set方法设置参数(效果跟Statement相似,相当于执行静态SQL)
String param = "'test' or 1=1";
String sql = "select file from file where name = " + param; // 拼接SQL参数
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
System.out.println(resultSet.next());
输出结果为true,DB中执行的SQL为
-- 永真条件1=1成为了查询条件的一部分,可以返回所有数据,造成了SQL注入问题
select file from file where name = 'test' or 1=1
- 使用PreparedStatement的set方法设置参数
String param = "'test' or 1=1";
String sql = "select file from file where name = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, param);
ResultSet resultSet = preparedStatement.executeQuery();
System.out.println(resultSet.next());
输出结果为false,DB中执行的SQL为
select file from file where name = '\'test\' or 1=1'
我们可以看到输出的SQL文是把==整个参数用引号包起来,并把参数中的引号作为转义字符==,从而避免了参数也作为条件的一部分。
具体分析
查看源码:com.mysql.cj.ClientPreparedQueryBindings#setString
public void setString(int parameterIndex, String x) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
// if the passed string is null, then set this column to null
if (x == null) {
setNull(parameterIndex, Types.CHAR);
} else {
checkClosed();
int stringLength = x.length();
if (this.connection.isNoBackslashEscapesSet()) {
// Scan for any nasty chars
// 判断是否需要转义处理(比如包含引号,换行等字符)
boolean needsHexEscape = isEscapeNeededForString(x, stringLength);
// 如果不需要转义,则在两边加上单引号
if (!needsHexEscape) {
byte[] parameterAsBytes = null;
StringBuilder quotedString = new StringBuilder(x.length() + 2);
quotedString.append('\'');
quotedString.append(x);
quotedString.append('\'');
...
} else {
...
}
}
String parameterAsString = x;
boolean needsQuoted = true;
// 如果需要转义,则做转义处理
if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) {
...
}
}
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 程序员小航
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果