本篇讲诉为何在JDBC操作数据库的过程中,要使用PreparedStatement对象来代替Statement对象。

  在前面的JDBC学习中,对于Statement对象,我们已经知道是封装SQL语句并发送给数据库执行的功能,但是实际开发中,这个功能我们更经常用的是Statement类的子类PreparedStatement类的对象来实现,而不是采用Statement对象。

  Statement和PreparedStatement的关系与区别在于:

  ① PreparedStatement类是Statement类的子类,拥有更多强大的功能。

  ② PreparedStatement类可以防止SQL注入攻击的问题,后面会说到。

  ③ PreparedStatement会对SQL语句进行预编译,以减轻数据库服务器的压力,而Statement则无法做到。

例1 :使用PreparedStatement对代码中Statement进行更换

  构建一张user表,接着我们要在程序中使用JDBC对数据库进行User对象数据的添加,先用Statement对象来展示,后使用PreparedStatement对象,以此来比较两者的不同。

  在MySQL数据库中新建jdbcdemo库,并构建user表:

1:创建一个数据库
create database jdbcdemo; 2:使用刚创建的数据库
use jdbcdemo; 3:创建一个user表
create table user(
id int primary key,
name varchar(40),
age int
);

  创建工程,在工程中导入数据库连接驱动的jar包。在【src】目录下新建一个database.properties文件,内容如下:

    driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcdemo
username=root
password=root

  构建JDBC的工具类,包括注册驱动,获取连接,释放资源和连接等,这部分同《JDBC操作数据库的学习(2)》中相同,代码如下:

 public class JdbcUtils {

     private static Properties config = new Properties(); 

     static{
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("database.properties");
try{
config.load(in);
Class.forName(config.getProperty("driver")); }catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
} public static Connection getConnection() throws SQLException {
String url = config.getProperty("url");
String username = config.getProperty("username");
String password = config.getProperty("password");
Connection conn = DriverManager.getConnection(url, username, password);
return conn;
} public static void release(Connection conn,Statement st,ResultSet rs) {
if(rs!=null) {
try{
rs.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(st!=null) {
try{
st.close();
}catch (Exception e) {
e.printStackTrace();
}
}
if(conn!=null) {
try{
conn.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
}

  要添加User对象,那必须在工程中定义的domain包中做出User类的JavaBean:

 public class User {
private int id;
private String name;
private int age;
。。。//省略getter方法和setter方法
}

  回归正题,例如我们需要在UserDao层的实现类UserDaoImpl中对User对象进行数据库的增删改查,这里仅展示对User对象的添加,先使用Statement对象:

 public class UserDaoImpl {
public void insert(User user) throws SQLException {
Connection conn = null;
Statement st = null;
ResultSet rs = null; try{
conn = JdbcUtils.getConnection();
st = conn.createStatement();
String sql = "insert into user(id,name,age) values("+user.getId()+",'"+user.getName()+"','"+user.getAge()+"')"; //这里简直让人奔溃
int num = st.executeUpdate(sql);
if(num>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}

-------------------------------------我是使用PreparedStatement的分割线-------------------------------

  使用PreparedStatement对上面例子的代码进行更改:

 public class UserDaoImpl {
public void insert(User user) throws SQLException {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "insert into user(id,name,age) values(?,?,?)";
st = conn.prepareStatement(sql);
st.setInt(1, user.getId());
st.setString(2, user.getName());
st.setInt(3, user.getAge());
int num = st.executeUpdate();
if(num>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
} }finally{
JdbcUtils.release(conn, st, rs);
}
}
}

  在上面的代码中,使用PreparedStatement与Statement代码不同的地方都已经用红字标出:

  ⑴ 首先我们在封装sql语句的对象应该使用PreparedStatement。

  ⑵ 我们在SQL语句的字符串中,以问号“?”来替代数据,相当于在国际化中的占位符,可以看到如果使用Statement的SQL语句字符串,要不断地使用字符串连接符,整个SQL字符串看上去简直让人奔溃。

⑶ 使用Connection对象的prepareStatement(String  sql)语句来创建PreparedStatement对象,在创建的工程中就要传入sql语句作为参数,这点和Statement不同。

  ⑷ 通过PreparedStatement对象的setXXX方法来对SQL语句字符串中的占位符进行替换,根据在数据库中的类型不同而采用不同的set方法。通过这种方式,我们可以 清晰地看清楚在SQL语句中每个占位符和一一对应在数据库中的列数据。

  ⑸ 最后使用PreparedStatement的executeUpdate()方法发送给数据库执行,注意和Statement不同,这里无需再向executeUpdate方法中传入参数,因为在创建PreparedStatement对象时已经传入了SQL字符串语句。如果使用查询executeQuery方法也是无需传入参数。

  通过上面的例子我们可以看到使用PreparedStatement在编写JDBC操作数据库的SQL字符串语句会非常好用,也使对应的数据显得非常清楚。

  不仅如此,PreparedStatement是能极大的减轻数据库服务器的压力的。从PreparedStatement的名字来看是叫“预编译”,如果要解释为什么预编译能优化数据库,那么就要从数据库说起,在操作数据库时,我们的每一条SQL语句都要在数据库中进行编译并执行,这一点和程序是类似的。对于JDBC来说,使用Statement对象那么只对SQL语句进行封装然后发送给数据库编译并执行,如果对数据库操作频繁,那么数据库都要做编译和执行两个步骤;而使用PreparedStatement对象的话,则由程序对SQL语句先进行编译,然后再发送给数据库服务器执行,这样就极大地减轻了数据库服务器的压力。

  除了能给数据库服务器带来极大的优化作用之外,PreparedStatement另外一个极大的功能就在于能防止SQL注入攻击。

  所谓的SQL注入,就是把SQL语句输入到WEB表单、或者输入域名或页面请求的查询字符串等等,在请求发送给服务器的过程中, 达到欺骗数据库服务器执行恶意的SQL命令。

例2:使用SQL注入进行用户登录

  要使SQL注入能成功,必须得是Statement对象才行。

  还是以例1创建user表为案例,如果我们开发好web层,做好前端页面显示,比如用户登录,就需要填写用户登录的用户名,而这里用户名就可以用恶意的SQL语句,假设我们在该处填写的用户名为:  ' or 1=1 or name='

  在点击提交或者登录按钮后,会以表单形式从web工程中层层传下,到dao层将表单的数据进行操作数据库,以匹配是否存在该用户,在dao层对User对象操作的UserDaoImpl实现类中,查找用户的代码如下:

 public class UserDaoImpl {
public User find(String name) throws SQLException {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select * from user where name='" +name+ "'";
st = conn.createStatement();
rs = st.executeQuery(sql);
if(rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setAge(rs.getInt("age"));
return user;
}else{
return null;
}
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}

  通过上述代码,经过测试,根据页面带过来的表单数据name值为' or 1=1 or name='是可以进行用户登录的。

  分析:我们若将表单填写的数据带到代码中的SQL语句,就形成如下的SQL命令:

  select * from user where name='' or 1=1 or name=''

可以看到使用Statement对象就是将两个字符串拼接形成的SQL语句,这样做很可能会将判断条件改变,如上面的命令,在where语句中出现了or  1=1 这样一定会返回true的语句,就如同程序发送一条“select * from user where true”的句子,那么数据库执行这条语句根本不需要筛选条件,只要数据库有任意用户,都可以告诉程序你找到了该指定用户,那么我们连密码都不用填的只需要恶意SQL语句即可登录网站。这就是一个SQL注入的典型例子。

  而使用PreparedStatement则不会,因为PreparedStatement的预编译,会将表单中所填写的数据进行编译,这种编译是包含字符过滤的编译,就好像对html进行过滤转义一样,这字符过滤最关键的因素在于PreparedStatement使用的是占位符,而不会像Statement那样因为拼接字符串而引入了引号,可以看到在PreparedStatement中即使接收的表单数据中SQL语句以引号包围,由于程序中的SQL语句使用占位符,因此就相当于条件为where name=' or 1=1 or name=',显然数据库并没有这样的记录,因此防止了SQL注入的问题。

  关于SQL注入也算是一门有趣的学问,建议平时对这方面多学习些,也有利于我们更好地加强自己开发中的安全性。

在JDBC中使用PreparedStatement代替Statement,同时预防SQL注入的更多相关文章

  1. mybatis中#{}与${}的差别(如何防止sql注入)

    默认情况下,使用#{}语法,MyBatis会产生PreparedStatement语句中,并且安全的设置PreparedStatement参数,这个过程中MyBatis会进行必要的安全检查和转义. # ...

  2. JAVA jdbc(数据库连接池)学习笔记(二) SQL注入

    PS:今天偶然间发现了SQL的注入...所以就简单的脑补了一下,都是一些简单的例子...这篇写的不怎么样...由于自己没有进行很深的研究... 学习内容: 1.SQL注入的概念...   所谓SQL注 ...

  3. 使用jdbc拼接条件查询语句时如何防止sql注入

    本人微信公众号,欢迎扫码关注! 使用jdbc拼接条件查询语句时如何防止sql注入 最近公司的项目在上线时需要进行安全扫描,但是有几个项目中含有部分老代码,操作数据库时使用的是jdbc,并且竟然好多都是 ...

  4. 利用PreparedStatement预防SQL注入

    1.什么是sql注入 SQL 注入是用户利用某些系统没有对输入数据进行充分的检查,从而进行恶意破坏的行为. 例如登录用户名采用  ' or 1=1 or username=‘,后台数据查询语句就变成 ...

  5. 转:JDBC中关于PreparedStatement.setObject的一些细节说明

    原文地址:https://blog.csdn.net/zhiyangxuzs/article/details/78657235 JDBC中PreparedStatement.setObject(ind ...

  6. Statement和PreparedStatement的区别; 什么是SQL注入,怎么防止SQL注入?

    问题一:Statement和PreparedStatement的区别 先来说说,什么是java中的Statement:Statement是java执行数据库操作的一个重要方法,用于在已经建立数据库连接 ...

  7. Statement和PreparedStatement的区别; 什么是SQL注入,怎么防止SQL注入? (转)

    问题一:Statement和PreparedStatement的区别 先来说说,什么是java中的Statement:Statement是java执行数据库操作的一个重要方法,用于在已经建立数据库连接 ...

  8. 一、JDBC的概述 二、通过JDBC实现对数据的CRUD操作 三、封装JDBC访问数据的工具类 四、通过JDBC实现登陆和注册 五、防止SQL注入

    一.JDBC的概述###<1>概念 JDBC:java database connection ,java数据库连接技术 是java内部提供的一套操作数据库的接口(面向接口编程),实现对数 ...

  9. mybatis中的#和$的区别 以及 防止sql注入

    声明:这是转载的. mybatis中的#和$的区别 1. #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号.如:order by #user_id#,如果传入的值是111,那么解析成sq ...

随机推荐

  1. Redis 突然报错 NOAUTH Authentication required

    查找相关资料,说是添加了密码 只需要在redis的配置文件redis.conf中开启requirepass就可以了,比如我设置我的访问密码是mypassword requirepass mypassw ...

  2. 关于PHPExcel类占用内存问题

    最近在帮一家公司做后台excel导出功能,使用的工具类是phpexcel,因为这个类功能比较强大.全面. 但是遇到下面一个问题: 当导出数据量达到一定数量级的时候,比如说1000条,服务器出现卡顿.白 ...

  3. mul8_unsigned multipliter

    李亚民老师更注重硬件设计思想的训练.他给出的硬件设计方法更贴近底层硬件,下面看看他的设计思想:                                                      ...

  4. 人人都用 Retina 屏幕的 MacBook Pro 笔记本电脑

    自从今年年初 Apple 官网产品降价我立即买了 13 寸的 Retina 屏 MacBook Pro(rMBP)之后, 这款苹果的笔记本电脑就成了我在公司和家里的唯一电脑(就是这一款). 使用苹果的 ...

  5. get get_children方法

    get 方法: [root@wx03 zook]# cat a4.pl use ZooKeeper; use AnyEvent; use AE; use Data::Dumper; my $zk = ...

  6. Linux远程桌面工具 -- NoMachine

    玩Linux系统,会经常用到远程桌面软件. 我一直用的2个是Xmanager 和 VNC. 今天看到一个新软件: NoMachine. NoMachine NX 是一个快速的终端服务器和虚拟桌面软件, ...

  7. 一年四个P(Project)

    盼望着,盼望着,提高班众多革命同胞的假期终于来了.伴随着校园之中越来越多的同学身影,暑假学习时的那份静谧一散而去,恍然间在提高班学习的第二个年头也已经过去了(~_~),而自己的大学生涯也就像秋后的蚂蚱 ...

  8. android——ListView功能的实现

    1.main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:a ...

  9. .NET截断字符串

    /// <summary> /// 截断字符串 /// </summary> /// <param name="s">要截断的字符串</p ...

  10. 02-OC方法、属性

    目录: 一.方法 二.实例变量 三.属性(点语法) 四.初始化方法(自定义构造方法) 回到顶部 一.方法 1 函数与方法有什么区别? 函数只是一个程序的代码段,与类无关. 方法,类的一部分,代表对象可 ...