篇1 介绍了使用JDBC 连接数据库及部分API 实现sql 查询,及数据库资源的回收,本篇主要是围绕 PrepaeredStatement对象的使用及Sql注入相关:
1.Sql 注入:
sql注入是比较常见的网络攻击形式,它利用现有的程序漏洞,将恶意的Sql命令注入后台数据库,最终达到欺骗服务器并实现攻击者的意图,在程序运行过程中,Sql注入会造成数据库信息泄露,网页被篡改,网站被挂木马等问题,
如接下来的两个Sql 语句:
Select * from 用户表 where name=用户输入的用户名 AND password=用户输入的密码;
Select * from 用户表 Where name="用户输入的用户名" AND password=12345 or 1=1;
此时,上述查询已基本失效,无论密码对错,用户均可以成功登陆,使得程序存在重大的安全隐患,这就是Sql注入 的一个具体场景。
篇1已经介绍过,Satement 对象可以通过3种方法向数据库发送Sql 语句,而这3种方法均是通过 传递字符类型的Sql 语句作为参数来实现的,由此可见,使用Statement 类会存在 SQL 注入的问题。
2.PreparedStatement 与Statement 对比:
PreparedStatement 是用来执行Sql 查询语句的API 之一,主要用来执行参数化的操作,PrepareStatement 对象可以对Sql 语句进行预编译,这可以有效的避免Sql 注入引发的问题,同时也提升了代码的可维护性和可读性:
它有一下优点:
1.PrepareStatement 能够执行参数化的Sql语句:
由于PrepareStatement 能够执行带参数化的Sql 语句,因此开发人员可以通过修改参数来反复调用同一个SQL 语句,这样可以避免反复书写相同的SQL 语句的繁琐。
参数化Sql 语句:
select count(1) from t_student where name=? and password = ?;
其中,“?” 是参数的占位符,程序可以通过传入不同的sid 值完成参数化查询
2. PrepareStatement 比Statement 效率更高:
使用PrepareStatement 时,数据库系统会对Sql语句进行预编译处理,执行计划同样会被缓存起来,这条预编译的SQL 语句能在将来的查询中重用,这样一来,它比Statement 对象生成的查询速度更快,性能更好,为了减少数据库的负载,在实际开发过程中一般使用PrepareStatement。
3.PrepareStatement 可以防止SQL 注入攻击
使用PrepareStatement 的参数化操作可以阻止大部分的Sql注入,在使用参数化查询的情况下,数据库系统不会将参数的内容视为SQL 指令的一部分来处理,而是在数据库完成SQL 指令的编译后,才套用参数执行。
使用PraparedStatement 对象通过executeUpate() 方法来实现·对数据库的写入,通过executeQuery() 方法实现对数据库的查询,使用PrepareStatement对象操作数据库的步骤与使用Statement 对象类似:
//解决sql注入的操作:
private static boolean login2(String name, String password) {
Connection conn = null;
PreparedStatement prst = null;
ResultSet rs = null;
try {
conn = DBUtils.getConnection();
//获取预处理的执行对象:
//好处:1.解决sql注入隐患问题 2.效率高
//?:代表占位符
String sql = "select count(1) from t_student where name=? and password=?";
prst = conn.prepareStatement(sql);
//-------给占位符赋值-------
prst.setString(1, name); //对应第一个?,参数下标从1开始
prst.setString(2, password); //对应第二个?
rs = prst.executeQuery();
if(rs.next()){
int result = rs.getInt(1); //获取第一个字段的值
return result>0; //如果大于0则为true
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtils.closeAll(rs,prst,conn);
}
return false;
}
4.使用PrepareStatement 对象实现批量处理:
在实际的开发过程中,有时需要向数据库送多条语句相同,但参数不同的SQL 语句,这是需要写上多条SQL 语句
为了避免重复发生相同的SQL语句,提升执行效率,在实际的开发过程中常采用PrepareStatement对象的批处理机制:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DBTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//加载并注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/renpengcheng";
String username = "root";
String password = "123456";
//获取数据库连接
Connection connection = DriverManager.getConnection(url, username, password);
//书写Sql语句
String sql = "insert into student (sname,age,course) values (?,?,?)";
//获取执行对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i <= 2; i++) {
preparedStatement.setString(1, "name" + i);
preparedStatement.setString(2, "22");
preparedStatement.setString(3, "java");
//添加进批
preparedStatement.addBatch();
//设置条件,当i对10取余为0时,先执行一次语句,然后将批清除
if (i % 10 == 0) {
preparedStatement.executeBatch();
preparedStatement.clearBatch();
}
}
//将批中剩余的语句执行完毕
preparedStatement.executeBatch();
//关闭资源
preparedStatement.close();
connection.close();
}
}