JDBC—03—SQL注入问题;PreparedStatement介绍;JDBC的事务处理


 

一、SQL 注入问题

(1)什么是 SQL 注入

所谓 SQL 注入,就是通过把 `含有 SQL 语句片段的参数` 插入到需要执行的 SQL 语句中,然后statement把SQL语句发送到数据库中, 数据库进行编译, 最终达到欺骗数据库服务器执行恶意操作的 SQL 命令。

(2)如何解决?

因为statement没有SQL语句预编译的能力, 所以会使数据库执行到恶意SQL命令;

我们换一个有预编译能力的statement对象就行了---PreparedStatement;

 

 

 

二、PreparedStatement 对象的使用(重点)

(1)PreparedStatement 特点:

  • PreparedStatement 接口继承 Statement 接口
  • PreparedStatement 效率高于 Statement
  • PreparedStatement 具备动态绑定参数能力
  • PreparedStatement 具备 SQL 语句预编译能力, 所以使用 PreparedStatement 可防止出现 SQL 注入问题

(2) 通过 PreparedStatement 对象向表中插入数据:

这个是需要拼接字符串的statement对象,我们可以看到,在拼接departmentName时,非常麻烦。

 那我们使用preparedStatement对象,

向 Departments 表中插入一条数据:


conn = JdbcUtil.getConnection();
//再也不用拼接字符串或者参数了, 参数用占位符`?`表示, 然后在单独对占位符赋值就好了; 
ps = conn.prepareStatement("insert into departments values(default,?,?)");
ps.setString(1, departmentName);
ps.setInt(2, locationId);
ps.execute();

 

 

 

三、 PreparedStatement 的预编译能力

1、什么是预编译

是先将带有占位符(即”?”)的sql模板发送至mysql服务器,由服务器对此无参数的sql进行编译后,将编译结果缓存,然后后面直接执行带有真实参数的sql,即允许数据库做参数化查询。

在使用参数化查询的情况下,数据库不会将参数的内容视为SQL执行的一部分而是作为一个字段的属性值来处理,这样就算参数中包含破环性语句(or ‘1=1’)也不会被执行。

 

(1)SQL 语句的执行步骤

  • 语法和语义解析
  • 优化 sql 语句,制定执行计划
  • 执行并返回结果

但是很多情况,我们的一条 sql 语句可能会反复执行,或者每次执行的时候只有个别的值不同(比如 select 的 where 子句值不同,update 的 set 子句值不同,insert 的 values 值不同)。

如果每次都需要经过硬解析----上面的语法语义解析、语句优化、制定执行计划等,那效率就明显不行了。

所谓预编译语句就是将这类语句中的值用占位符替代,可以视为将 sql 语句模板化或者说参数化;

预编译语句的优势在于:

  • 不用麻烦的拼接字符串
  • 一次编译、多次运行,省去了解析优化等过程(将完全硬解析变成了硬软都有);
  • 此外预编译语句能防止 sql 注入;

(2)解析方式

  • 硬解析:在不开启缓存执行计划的情况下,每次 SQL 的处理都要经过:语法和语义的解析,优化器处理 SQL,生成执行计划。整个过程我们称之为硬解析。
  • 软解析如果开启了缓存执行计划,数据库在处理 sql 时会先查询缓存中是否含有与当前SQL语句相同的执行计划,如果有则直接执行该计划,如果没有先执行硬解析,然后将这次的硬解析加入缓存中。(我们所谓的预编译也就是软解析;)

2、预编译方式

  • 开始数据库的日志
  • show VARIABLES like '%general_log%'
  • set GLOBAL general_log = on
  • set GLOBAL log_output='table'

(1)依赖数据库驱动完成预编译

如果我们没有开启数据库服务端编译,那么默认的是使用数据库驱动完成 SQL 的预编

译处理。

(2)依赖数据库服务器完成预编译

我们可以通过修改连接数据库的 URL 信息,添加 useServerPrepStmts=true 信息开启服

务端预编译。

 

关于预编译,此作者写的挺好https://www.cnblogs.com/qiumingcheng/p/8060471.html

 

 

 

四、通过 PreparedStatement 对象完成数据的操作

1、对数据的更新:

更新数据

conn = JdbcUtil.getConnection();

ps = conn.prepareStatement("update departments set department_name = ?,location_id = ? where department_id = ?");

ps.setString(1, departmentName);

ps.setInt(2, localhostId);

ps.setInt(3, departmentId);

ps.execute();

2、对数据的查询

(1)查询返回单条结果集


conn = JdbcUtil.getConnection();

ps = conn.prepareStatement("select * from

departments where department_id = ?");

ps.setInt(1, departmentId);

rs = ps.executeQuery();

while(rs.next()){

dept=new Departments();

dept.setDepartmentId(rs.getInt("department_id"));

dept.setDepartmentName(rs.getString("department_name"));

dept.setLocationId(rs.getInt("location_id"));

}

(2)查询返回多条结果集

建立了一个集合而已, 然后不断地输出这个集合的信息就可以了; 

这里有个bug,大家找找...

 上图有个bug,  大家可以试着找找看;😂

既然是模糊查询, 怎么能用`=`, 应该用 `like`;

 

 

 

 

 

3. PreparedStatement 批处理操作

批处理:在与数据库的一次连接中,批量的执行条 SQL 语句。

代码:

注意:addBatch()和executeBatch();

 

 

 

 

 

 

五、JDBC 中的事务处理

在 JDBC 操作中数据库事务默认为自动提交。

如果事务需要修改为手动提交,那么我们需要使用 Connection 对象中的 setAutoCommit 方法来关闭事务自动提交, 然后通过Connection 对象中的 commit 方法与 rollback 方法进行事务的提交与回滚。

 

posted @ 2019-09-27 10:57  Eric-Shen  阅读(290)  评论(0编辑  收藏  举报