PreparedStatement

l 它是Statement接口的子接口;

l 强大之处:

  • 防SQL攻击;
  • 提高代码的可读性、可维护性;
  • 提高效率!

l 学习PreparedStatement的用法:

  • 如何得到PreparedStatement对象:

¨ 给出SQL模板!

¨ 调用Connection的PreparedStatement prepareStatement(String sql模板);

¨ 调用pstmt的setXxx()系列方法sql模板中的?赋值!

¨ 调用pstmt的executeUpdate()或executeQuery(),但它的方法都没有参数。

l 预处理的原理

  • 服务器的工作:

¨ 校验sql语句的语法!

¨ 编译:一个与函数相似的东西!

¨ 执行:调用函数

  • PreparedStatement:

¨ 前提:连接的数据库必须支持预处理!几乎没有不支持的!

¨ 每个pstmt都与一个sql模板绑定在一起,先把sql模板给数据库,数据库先进行校验,再进行编译。执行时只是把参数传递过去而已!

¨ 若二次执行时,就不用再次校验语法,也不用再次编译!直接执行!

1 什么是SQL攻击

在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们DAO中写的SQL语句合成一个完整的SQL语句!例如用户在登录时输入的用户名和密码都是为SQL语句的片段!

2 演示SQL攻击

首先我们需要创建一张用户表,用来存储用户的信息。

CREATE TABLE user(

uidCHAR(32) PRIMARY KEY,

usernameVARCHAR(30) UNIQUE KEY NOT NULL,

PASSWORD VARCHAR(30)

);

INSERT INTO user VALUES('U_1001', 'zs', 'zs');

SELECT * FROM user;

现在用户表中只有一行记录,就是zs。

下面我们写一个login()方法!

public void login(String username, String password) {

Connection con = null;

Statement stmt = null;

ResultSet rs = null;

try {

con = JdbcUtils.getConnection();

stmt = con.createStatement();

String sql = "SELECT * FROM user WHERE " +

"username='" + username +

"' and password='" + password + "'";

rs = stmt.executeQuery(sql);

if(rs.next()) {

System.out.println("欢迎" + rs.getString("username"));

} else {

System.out.println("用户名或密码错误!");

}

} catch (Exception e) {

throw new RuntimeException(e);

} finally {

JdbcUtils.close(con, stmt, rs);

}

}

下面是调用这个方法的代码:

login("a' or 'a'='a", "a' or 'a'='a");

这行当前会使我们登录成功!因为是输入的用户名和密码是SQL语句片段,最终与我们的login()方法中的SQL语句组合在一起!我们来看看组合在一起的SQL语句:

SELECT * FROM tab_user WHERE username='a' or 'a'='a' and password='a' or 'a'='a'

3 防止SQL攻击

l 过滤用户输入的数据中是否包含非法字符;

l 分步交验!先使用用户名来查询用户,如果查找到了,再比较密码;

l 使用PreparedStatement。

4 PreparedStatement是什么?

PreparedStatement叫预编译声明!

PreparedStatement是Statement的子接口,你可以使用PreparedStatement来替换Statement。

PreparedStatement的好处:

l 防止SQL攻击;

l 提高代码的可读性,以可维护性;

l 提高效率。

5 PreparedStatement的使用

l 使用Connection的prepareStatement(String sql):即创建它时就让它与一条SQL模板绑定;

l 调用PreparedStatement的setXXX()系列方法为问号设置值

l 调用executeUpdate()或executeQuery()方法,但要注意,调用没有参数的方法;

String sql = “select * from tab_student where s_number=?”;

PreparedStatement pstmt = con.prepareStatement(sql);

pstmt.setString(1, “S_1001”);

ResultSet rs = pstmt.executeQuery();

rs.close();

pstmt.clearParameters();

pstmt.setString(1, “S_1002”);

rs = pstmt.executeQuery();

在使用Connection创建PreparedStatement对象时需要给出一个SQL模板,所谓SQL模板就是有“?”的SQL语句,其中“?”就是参数。

在得到PreparedStatement对象后,调用它的setXXX()方法为“?”赋值,这样就可以得到把模板变成一条完整的SQL语句,然后再调用PreparedStatement对象的executeQuery()方法获取ResultSet对象。

注意PreparedStatement对象独有的executeQuery()方法是没有参数的,而Statement的executeQuery()是需要参数(SQL语句)的。因为在创建PreparedStatement对象时已经让它与一条SQL模板绑定在一起了,所以在调用它的executeQuery()和executeUpdate()方法时就不再需要参数了。

PreparedStatement最大的好处就是在于重复使用同一模板,给予其不同的参数来重复的使用它。这才是真正提高效率的原因。

所以,建议大家在今后的开发中,无论什么情况,都去需要PreparedStatement,而不是使用Statement。

useServerPrepStmts参数

默认使用PreparedStatement是不能执行预编译的,这需要在url中给出useServerPrepStmts=true参数(MySQL Server 4.1之前的版本是不支持预编译的,而Connector/J在5.0.5以后的版本,默认是没有开启预编译功能的)。

例如:jdbc:mysql://localhost:3306/test?useServerPrepStmts=true

  这样才能保证mysql驱动会先把SQL语句发送给服务器进行预编译,然后在执行executeQuery()时只是把参数发送给服务器。

cachePrepStmts参数

当使用不同的PreparedStatement对象来执行相同的SQL语句时,还是会出现编译两次的现象,这是因为驱动没有缓存编译后的函数key,导致二次编译。如果希望缓存编译后函数的key,那么就要设置cachePrepStmts参数为true。例如:

jdbc:mysql://localhost:3306/test?useServerPrepStmts=true&cachePrepStmts=true

SQL攻击-预编译--缓存的更多相关文章

  1. 测试mysql的sql语句预编译效果

    玩Oracle的都比较关注shared pool,特别是library cache,在使用了绑定变量(预编译sql)之后确实能得到很大的性能提升.现在在转Mysql之后特别是innodb很多东西都还能 ...

  2. SQL Server 预编译执行SQLs

    问题描述: MVC5项目,利用执行sql的方式获取数据,但是在利用预编译执行的时候报错了,字段XXXwhich was not supplied. 其实就是这个参数传了个null导致的.在传参数之前做 ...

  3. sql的预编译问题

  4. mybatis深入理解之 # 与 $ 区别以及 sql 预编译

    mybatis 中使用 sqlMap 进行 sql 查询时,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,sql 如下: select * from user where name = ...

  5. 为什要使用预编译SQL?(转)

    本文转自https://www.cnblogs.com/zouqin/p/5314827.html 今天在研发部技术大牛的指点下,我终于明白了为什么要使用SQL预编译的形式执行数据库JDBC:

  6. mybatis之 # 与 $ 区别以及 sql 预编译

    mybatis 中使用 sqlMap 进行 sql 查询时,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,sql 如下: select * from user where name = ...

  7. mybatis深入理解(一)之 # 与 $ 区别以及 sql 预编译

    mybatis 中使用 sqlMap 进行 sql 查询时,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,sql 如下: select * from user where name = ...

  8. SQL或HQL预编译语句,可以防止SQL注入,可是不能处理%和_特殊字符

    近期项目在做整改,将全部DAO层的直接拼接SQL字符串的代码,转换成使用预编译语句的方式.个人通过写dao层的单元測试,有下面几点收获. dao层代码例如以下 //使用了预编译sql public L ...

  9. 转自“脚本之家”!!JDBC之PreparedStatement类中预编译的综合应用解析

    JDK 文档:SQL 语句被预编译并存储在 PreparedStatement 对象中(PreparedStatement是存储在JDBC里的,初始化后,缓存到了JDBC里),然后可以使用此对象多次高 ...

随机推荐

  1. hdu 3530 "Subsequence" (单调队列)

    传送门 题意: 给出一个序列,求最长的连续子序列,使得 m ≤ Max-Min ≤ k 我的理解: 定义数组 a[] 存储输入的 n 个数: 定义两个双端队列: deque<int >qM ...

  2. Maven 命令参数 整理

    命令参数 备注 mvn -v --version 显示版本信息; mvn -V --show-version 显示版本信息后继续执行Maven其他目标; mvn -h --help 显示帮助信息; m ...

  3. 用go实现一个redis-cli

    转载文章:https://my.oschina.net/liangwt/blog/2231557?origin= 代码样例:https://github.com/liangwt/redis-cli

  4. OS + RedHat 6.3 x64 NFS / mount.nfs: access denied by server while mounting

    s Linux mount/unmount命令(转) https://www.cnblogs.com/xd502djj/p/3809375.html 问题2:NFS配置项no_root_squash和 ...

  5. springboot入门使用

    一.什么是springboot,有什么用 springboot是一个开发框架,其出现的目的利用约定大于配置的思想来让开发者摆脱spring繁琐的配置,简化开发.其不是spring框架的替代品,是spr ...

  6. shell常见文本处理。(awk 替换换行符等)

    1 文件里有如下行,我想将每行的回车符替换为逗号,并将所有行合并到一行,用awk或sed怎么写啊TOP_COLUMNTOP_MESSAGETOP_OPTIONSTOP_TOPICTOP_VOTETOP ...

  7. eclipse没有server选项

    摘录自:https://www.cnblogs.com/xiaoxiaoweng/p/7298183.html 问题描述:eclipse没有server选项 解决问题:找到Help->Insta ...

  8. 15、JDBC-CallableStatement

    一.存储过程 创建 CREATE DEFINER=CURRENT_USER PROCEDURE `adder`(IN a int, IN b int, OUT sum int) BEGIN DECLA ...

  9. 加密PDF文件的打印问题

    工作中遇到网上下载的PDF文件加密,并且不能打印 解决方法: 1.解密: 去网站下载解密软件,1M左右:http://www.onlinedown.net/soft/19939.htm 直接解压,运行 ...

  10. DirectX11 With Windows SDK--03 索引缓冲区、常量缓冲区

    前言 一个立方体有8个顶点,然而绘制一个立方体需要画12个三角形,如果按照前面的方法绘制的话,则需要提供36个顶点,而且这里面的顶点数据会重复4次甚至5次.这样的绘制方法会占用大量的内存空间. 接下来 ...