• 在jdbc中,有三种方式执行sql,分别是使用Statement(sql拼接),PreparedStatement(预编译),还有一种CallableStatement(存储过程),在这里我就不介绍CallableStatement了,我们来看看Statement与PreparedStatement的区别。
1. 创建数据库,数据表

数据库名字是test,数据表的名字是student,里面有四个字段,一个是id,也就是主键(自动递增),还有名字,年龄,成绩。最后先使用sql语句插入六个测试记录。

CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE `student` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(20) NOT NULL ,
`age` INT NOT NULL , `score` DOUBLE NOT NULL , PRIMARY KEY (`id`)) ENGINE = MyISAM;
INSERT INTO `student` VALUES (1, '小红', 26, 83);
INSERT INTO `student` VALUES (2, '小白', 23, 93);
INSERT INTO `student` VALUES (3, '小明', 34, 45);
INSERT INTO `student` VALUES (4, '张三', 12, 78);
INSERT INTO `student` VALUES (5, '李四', 33, 96);
INSERT INTO `student` VALUES (6, '魏红', 23, 46);

建立对应的学生类:

/**
* student类,字段包括id,name,age,score
* 实现无参构造,带参构造,toString方法,以及get,set方法
* @author 秦怀
*/
public class Student {
private int id;
private String name;
private int age;
private double score; public Student() {
super();
// TODO Auto-generated constructor stub
}
public Student(String name, int age, double score) {
super();
this.name = name;
this.age = age;
this.score = score;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age
+ ", score=" + score + "]";
} }
2.Statement

先来看代码,下面是获取数据库连接的工具类 DBUtil.class

public class DBUtil {
private static String URL="jdbc:mysql://127.0.0.1:3306/test";
private static String USER="root";
private static String PASSWROD ="123456";
private static Connection connection=null;
static{
try {
Class.forName("com.mysql.jdbc.Driver");
// 获取数据库连接
connection=DriverManager.getConnection(URL,USER,PASSWROD);
System.out.println("连接成功");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 返回数据库连接
public static Connection getConnection(){
return connection;
}
}

下面是根据id查询学生信息的代码片段,返回student对象就能输出了:

	public Student selectStudentByStatement(int id){
// 拼接sql语句
String sql ="select * from student where id = "+id;
try {
// 获取statement对象
Statement statement = DBUtil.getConnection().createStatement();
// 执行sql语句,返回 ResultSet
ResultSet resultSet = statement.executeQuery(sql);
Student student = new Student();
// 一条也只能使用resultset来接收
while(resultSet.next()){
student.setId(resultSet.getInt("id"));
student.setName(resultSet.getString("name"));
student.setAge(resultSet.getInt("age"));
student.setScore(resultSet.getDouble("score"));
}
return student;
} catch (SQLException e) {
// TODO: handle exception
}
return null;
}

我们可以看到整个流程是先获取到数据库的连接Class.forName("com.mysql.jdbc.Driver"); connection=DriverManager.getConnection(URL,USER,PASSWROD);获取到连接之后通过连接获取statement对象,通过statement来执行sql语句,返回resultset这个结果集,Statement statement = DBUtil.getConnection().createStatement();ResultSet resultSet = statement.executeQuery(sql);,值得注意的是,上面的sql是已经拼接好,写固定了的sql,所以很容易被注入,比如这句:

sql = "select * from user where name= '" + name + "' and password= '" + password+"'";

如果有人

  • name = "name' or '1'= `1"
  • password = "password' or '1'='1",那么整个语句就会变成:
sql = "select * from user where name= 'name' or '1'='1' and password= 'password' or '1'='1'";

那么就会返回所有的信息,所以这是很危险的。

还有更加危险的,是在后面加上删除表格的操作,不过一般我们都不会把这些权限开放的。

// 如果password = " ';drop table user;select * from user where '1'= '1"
// 后面一句不会执行,但是这已经可以删除表格了
sql = "select * from user where name= 'name' or '1'='1' and password= '' ;drop table user;select * from user where '1'= '1'";

所以预编译显得尤为重要了。

3.PreparedStatement预编译

我们先来看看预编译的代码:

	// 根据id查询学生
public Student selectStudent(int id){
String sql ="select * from student where id =?";
try {
PreparedStatement preparedStatement = DBUtil.getConnection()..prepareStatement(sql);
preparedStatement.setInt(1, id);
ResultSet resultSet = preparedStatement.executeQuery();
Student student = new Student();
// 一条也只能使用resultset来接收
while(resultSet.next()){
student.setId(resultSet.getInt("id"));
student.setName(resultSet.getString("name"));
student.setAge(resultSet.getInt("age"));
student.setScore(resultSet.getDouble("score"));
}
return student;
} catch (SQLException e) {
// TODO: handle exception
}
return null;
}

预编译也是同样需要获取到数据库连接对象connection,但是sql语句拼接的时候使用了占位符?,将含有占位符的sql当参数传进去,获取到PreparedStatement预编译的对象,最后是通过set来绑定参数,然后再去使用execute执行预编译过的代码。这样就避免了sql注入的问题,同时,由于sql已经编译过缓存在数据库中,所以执行起来不用再编译,速度就会比较快。

4.为什么预编译可以防止sql注入
  • 在使用占位符,或者说参数的时候,数据库已经将sql指令编译过,那么查询的格式已经订好了,也就是我们说的我已经明白你要做什么了,你要是将不合法的参数传进去,会有合法性检查,用户只需要提供参数给我,参数不会当成指令部分来执行,也就是预编译已经把指令以及参数部分区分开,参数部分不允许传指令进来。

    这样的好处查询速度提高,因为有了预编译缓存,方便维护,可读性增强,不会有很多单引号双引号,容易出错,防止大部分的sql注入,因为参数和sql指令部分数据库系统已经区分开。百度文库里面提到:传递给PreparedStatement对象的参数可以被强制进行类型转换,使开发人员可以确保在插入或查询数据时与底层的数据库格式匹配。

    要是理解不透彻可以这么来理解:
select * from student where name= ?

预编译的时候是先把这句话编译了,生成sql模板,相当于生成了一个我知道你要查名字了,你把名字传给我,你现在想耍点小聪明,把字符串'Jame' or '1=1'传进去,你以为他会变成下面这样么:

select * from student where name= 'Jame' or '1=1'

放心吧,不可能的,这辈子都不可能的啦,数据库都知道你要干嘛了,我不是有sql模板了么,数据库的心里想的是我叫你传名字给我,行,这名字有点长,想害我,可以,我帮你找,那么数据库去名字这一字段帮你找一个叫'Jame' or '1=1'的人,他心里想这人真逗,没有这个人,没有!!!

所以这也就是为什么预编译可以防止sql注入的解释了,它是经过了解释器解释过的,解释的过程我就不啰嗦了,只要是对参数做转义,转义之后让它在拼接时只能表示字符串,不能变成查询语句。

此文章仅代表自己(本菜鸟)学习积累记录,或者学习笔记,如有侵权,请联系作者删除。人无完人,文章也一样,文笔稚嫩,在下不才,勿喷,如果有错误之处,还望指出,感激不尽~

技术之路不在一时,山高水长,纵使缓慢,驰而不息。

公众号:秦怀杂货店

JDBC【4】-- jdbc预编译与拼接sql对比的更多相关文章

  1. [疯狂Java]JDBC:PreparedStatement预编译执行SQL语句

    1. SQL语句的执行过程——Statement直接执行的弊病: 1) SQL语句和编程语言一样,仅仅就会普通的文本字符串,首先数据库引擎无法识别这种文本字符串,而底层的CPU更不理解这些文本字符串( ...

  2. JDBC编程之预编译SQL与防注入式攻击以及PreparedStatement的使用教程

      转载请注明原文地址: http://www.cnblogs.com/ygj0930/p/5876951.html 在JDBC编程中,常用Statement.PreparedStatement 和  ...

  3. JDBC编程之预编译SQL与防注入

    在JDBC编程中,常用Statement.PreparedStatement 和 CallableStatement三种方式来执行查询语句,其中 Statement 用于通用查询, PreparedS ...

  4. mybatis以及预编译如何防止SQL注入

    SQL注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中(例如,为了转储数据库内容给攻击者).[摘自] SQL injection - Wikipedia SQL ...

  5. Python预编译语句防止SQL注入

    这个月太忙,最近不太太平,我的愿望是世界和平! ================================== 今天也在找python的预编译,早上写的sql是拼接来构成的.于是找了2篇文章,还 ...

  6. 预编译对象解决SQL注入问题

  7. Java JDBC下执行SQL的不同方式、参数化预编译防御

    相关学习资料 http://zh.wikipedia.org/wiki/Java数据库连接 http://lavasoft.blog.51cto.com/62575/20588 http://blog ...

  8. Java学习笔记——JDBC之PreparedStatement类中“预编译”的综合应用

    预编译 SQL 语句被预编译并存储在 PreparedStatement 对象中.然后可以使用此对象多次高效地执行该语句. 预编译的优点 1.PreparedStatement是预编译的,对于批量处理 ...

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

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

  10. 预编译SQL为什么能够防止SQL注入

    前言 之前我一个搞网络安全的朋友问了我一个的问题,为啥用 PreparedStatement 预编译的 SQL 就不会有被 SQL 注入的风险? 第一时间我联想到的是八股文中关于 Mybatis 的脚 ...

随机推荐

  1. Serilog文档翻译系列(三) - 基础配置

    Serilog 使用简单的 C# API 来配置日志记录.当需要外部配置时,可以(慎用)通过使用 Serilog.Settings.AppSettings 包或 Serilog.Settings.Co ...

  2. Failed to convert value of type 'java.lang.String' to required type

    DEBUG 微信小程序Java后台 Failed to convert value of type 'java.lang.String' to required type 产生这种条件的原因一般是使用 ...

  3. [OI] 二项式期望 DP

    OSU OSU yet Another OSU yet yet Another OSU OSU 的题目是这样的:有一些相邻的块,给定每一个块的联通概率,每个连通块对答案有 \(size^{3}\) 的 ...

  4. トヨタ自動車プログラミングコンテスト2024#7(ABC 362)

    非常好名次,使我的 \(1\) 旋转 四发罚时应该是这次比赛最唐的东西了,没有就进前一千了 A.Buy a Pen 特判秒了,懒得打三种 ans=,所以就把不能选的那个赋值成无穷大了 #include ...

  5. C++的并发编程历史

    多线程环境 并非所有的语言都提供了多线程的环境.即便是C++语言,直到C++11标准之前,也是没有多线程支持的. 在这种情况下,Linux/Unix平台下的开发者通常会使用POSIX Threads, ...

  6. MobileNet V2中InvertedResidual实现

    1.为了方便理解其本身结构,找到源码理解一下. 2.论文地址:http://arxiv.org/pdf/1801.04381.pdf 3.V2相比较V1增加了倒残差结构和线性瓶颈层.整个结构按照维度来 ...

  7. iOS程序内语言切换使用小结

    随着时代的发展,应用程序相继出现了不同语言的版本方案,中文,英文,法文,韩文等等:想在应用程序中实现语言的自由切换,需要配置多个语言的文件,根据用户的动态选择获取不同语言文件下的语言文件,从而显示到界 ...

  8. windows下安装部署 hadoop

    一.安装下载 1.首先在hadoop官网下载一个稳定版本,选择binary包 官网地址:https://hadoop.apache.org/releases.html 下载下来是tar.gz文件,用w ...

  9. 云原生周刊:LitmusChaos 审计完成|2024.9.2

    开源项目推荐 Gardener Gardener 实现了 Kubernetes 集群的自动化管理和操作服务,并提供了一个经过完全验证的可扩展性框架,可以调整以适应任何编程云或基础设施提供商. Graf ...

  10. Derivative norm vector repect to time 《PBM by Pixar》 Appendix D.2 code

    目录 1 Derivative normal vector repect to time 1.1 Derivative vector norm repect to time X Ref Vector ...