JDBC中Statement和PrepareStatement的区别及特性
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC中Statement和PrepareStatement的区别及特性相关的知识,希望对你有一定的参考价值。
statement是语句,prepared statement是预定义语句。在Visual Basic中的基本语句包括:
一、赋值语句。
赋值语句的语法如下:
变量名或对象.属性=表达式
它的含义是把等号右边的值赋给等号左边的值。
二、判定结构。
一、If语句。
用If...Then结构有条件地执行一个或多个语句。单行语法和多行块语法都可以使用:
If condition Then statement
If condition Then
Statements
End If
Condition 通常是比较式,但它可以是任何计算数值的表达式。Visual Basic 将这个值解释为True或False:一个为零的数值为False,而任何非零数值都被看作True。若condition为True,则Visual Basic执行Then
关键字后面的所有statements。可以使用单行或多行语法有条件地执行一个语句。
注意:If...Then的单行格式不用End If语句。如果condition为True时要执行多行代码,则必须使用多行块If...Then...End If语法。
二、If...Then...Else语句。
用If...Then...Else块定义几个语句块,执行其中一个语句:
If condition1 Then
[statementblock-1]
[ElseIf condition2 Then
[statementblock-2]] ...
[Else
[statementblock-n]]
End If
Visual Basic首先测试condition1。如果它为False,Visual Basic就测试
condition2,依次类推,直到找到一个为True的条件。当它找到一个为
True的条件时,Visual Basic就会执行相应的语句块,然后执行End If后面的代码。作为一个选择,可以包含Else语句块,如果条件都不是True,则Visual Basic执行Else语句块。
If...Then…ElseIf只是If...Then...Else的一个特例。注意,可以使用任意数量的
ElseIf子句,或者一个也不用。可以有一个Else子句,而不管有没有ElseIf
子句。
二、循环语句。
电脑最擅长的就是不厌其烦地重复做一项工作成千上万遍(即重复执行几行代码),这就是通过循环结构来完成的。VB支持的循环结构有:Do…Loop和For…Next。
用Do循环重复执行一个语句块,且重复次数不定。Do…Loop是以计算数值为条件以决定是否继续执行。条件必须是一个数值或者值为True或False的表达式。
在下面的Do…Loop循环中,只要条件为真就执行循环。
Do While 循环条件
循环语句块
Loop
当Visual Basic执行到这个Do循环时首先测试条件,条件为假时,跳过所有语句。如果条件为真,Visual Basic就会执行语句,退回到Do While语句测试条件。只要条件为真,循环可以随意执行几次。如果条件一开始便为假,则不会执行语句。
还有一种Do…Loop语句,是先执行语句,每次执行之后测试条件,循环中的语句至少执行一次。
Do
循环语句块
Loop While 循环条件
在不知道循环要执行几次语句时,用Do循环,知道循环次数时,可以使用For…Next循环。For循环使用一个叫做计数器的变量,重复一次循环之后,计数器的值会增加或减少。
For 计数器=初值To终止值Step增量
循环语句块
Next 计数器
计数器、初值、终止值和增量为数值型。执行For循环时,设置计数器等于初值,测试计数器是否大于终止值,是则退出循环,执行循环语句,计数器增加增量后重复以上步骤。
用Exit语句可以退出For循环、Do循环,它的语法是Exit Do和Exit For,在循环中出现的次数无限制。 参考技术A 主要区别:
Statement执行一条sql就得编译一次,PrepareStatement只编译一次;常用后者原因在于参数设置非常方便;执行一条sql就得编译一次,后者只编译一次;还有就是sql放置的位置不同; 常用后者原因在于参数设置非常方便。
jdbc的api中的主要的四个类之一的java.sql.statement要求开发者付出大量的时间和精力。在使用statement获取jdbc访问时所具有的一个共通的问题是输入适当格式的日期和时间戳。
通过使用Java.sql.preparedstatement,这个问题可以自动解决。一个preparedstatement是从 java.sql.connection对象和所提供的sql字符串得到的,sql字符串中包含问号(?),这些问号标明变量的位置,然后提供变量的值, 最后执行语句,例如:
stringsql = "select * from people p where p.id = ? and p.name = ?";
preparedstatement ps = connection.preparestatement(sql);
ps.setint(1,id);
ps.setstring(2,name);
resultset rs = ps.executequery();
使用preparedstatement的另一个优点是字符串不是动态创建的。下面是一个动态创建字符串的例子:
stringsql = "select * from people p where p.i = "+id;
preparedstatement也提供数据库无关性。当显示声明的sql越少,那么潜在的sql语句的数据库依赖性就越小。
由于preparedstatement具备很多优点,开发者可能通常都使用它,只有在完全是因为性能原因或者是在一行sql语句中没有变量的时候才使用通常的statement。 参考技术B Statement用于执行静态 SQL 语句并返回它所生成结果的对象。PreparedStatement是Statement的子接口,表示预编译的 SQL 语句的对象。
JDBC基础--[JDBC概述,JDBC的搭建,PreparedStatement和Statement执行SQL语句,结果集处理]
JDBC概述
Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。
由一组用Java语言编写的类和接口组成
对于数据库的操作无非就是增删改查的操作;java语言的开发者并不会直接进行数据库的操作;而是定义了一些接口,最终是由数据库的开发商去完成这些接口的具体实现.
所以也就可以访问不同的数据库.
JDBC API主要位于JDK中的java.sql包中(之后扩展的内容位于javax.sql包中),主要包括(斜体代表接口,需驱动程序提供者来具体实现):
DriverManager类:负责加载(管理)各种不同驱动程序(Driver),并根据不同的请求,向调用者返回相应的数据库连接(Connection).
Driver:驱动程序,会将自身加载到DriverManager中去,并处理相应的请求并返回相应的数据库连接(Connection)。
Connection接口 :与特定数据库的连接,负责与进行数据库间通讯,SQL执行以及事务处理都是在某个特定Connection环境中进行的。可以产生用以执行SQL的Statement。
Statement接口 :用以执行SQL查询和更新,向数据库发送SQL语句(针对静态SQL语句和单次执行)。
PreparedStatement接口:用以执行包含动态参数的SQL查询和更新(在服务器端编译,允许重复执行以提高效率)。
CallableStatement:用以调用数据库中的存储过程。
ResultSet接口:接收查询结果;表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
SQLException:代表在数据库连接的建立和关闭和SQL语句的执行过程中发生了例外情况(即错误)。
JDBC搭建
首先;在项目栏新建文件夹;(一般命名为lib有library的意思
)
将需要的数据库实现类的jar包复制到该文件夹;
1.导入jar包;
首先点击 File -> Project Structure(快捷键 Ctrl + Alt + Shift + s);找到左侧的选项Libraries;点击加号;点击Java
然后找到对应的项目存放地址;找到jar包,选择导入.
2.加载驱动;(在jdbc8之后,可以直接不写;会自动地加载)
这需要初始化驱动程序,这样就可以打开与数据库的通信信道。
方式1:
//java的反射机制,动态加载类;
Class.forName("com.mysql.cj.jdbc.Driver");
方式2:
DriverManager.registerDriver(new Driver());
在jdbc8之后,这些操作可以不用;会自动地加载驱动.
3.建立与数据库的连接
这里需要用到的是DriverManager类中的getConnection方法;代表一个物理连接的数据库.里面有三个参数;
url:ip和端口以及过滤字符编码,时区;
user:数据库用户名;
password:数据库密码.
返回的是Connection的对象,实际上是Connection实现类的对象;
//url的参数
jdbc:mysql://127.0.0.1(ip地址):3306(端口号)/(数据库名)?characterEncoding=utf8(字符编码过滤)&useSSL=false&serverTimezone=UTC(时区)
例如:
//建立与数据库studentworld_db的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//这里的Connection对象作为与数据库连接的通道;
Connection connection=DriverManager.getConnection(url,"root","123456");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
若运行无异常;则说明连接成功.注意,一定要准确数据库名,用户名,密码
4.向数据库发送SQL语句 在发送结束后,要记得关闭通道
PreparedStatement和Statement
使用Statement接口
首先是创建对象Statement st=connection.createStatement( );
使用executeUpdate方法执行SQL语句
这个Statement接口的executeUpdate方法有int类型的返回值;
发送DDL语句时,返回的是数字0;
发送DML语句(增,删,改)时,返回的是操作的行数.
例如;向数据库studentworld_db的数据表t_course下插入数据;(使用DML语句)
public class Demo01 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection=DriverManager.getConnection(url,"root","123456");
//向数据库发送SQL语句;
Statement st=connection.createStatement();
st.executeUpdate("insert into t_course(cou_id,Cname) values(108,'数分'),(109,'英语')");
//关闭数据库连接通道;
st.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
返回值为2;即插入了两行数据.
查看数据库这边,已经成功插入数据;
模拟数据库发送数据;
例如:向数据库studentworld_db的学生表t_student中传入数据;
使用Statement接口
注意要用连接符号把数据一个一个连接起来;字符串类型的用单引号,双引号,双加号包裹起来;数值类型的用双引号,双加号即可;
public class Demo02 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection= DriverManager.getConnection(url,"root","123456");
//向数据库发送SQL语句;
Statement st=connection.createStatement();
//模拟向数据库输入动态数据;
//以数据库的t_student表为例;
String name="大话王";
String sex="男";
String birthday="2018-5-7";
String phone="12345665412";
int gra_id=1805;
java.util.Date date=new java.util.Date();
java.sql.Timestamp res_time= new java.sql.Timestamp(date.getTime());
int score=82;
//向数据库发送数据;
//注意要用连接符号把数据一个一个连接起来;;字符串类型的用单引号,双引号,双加号包裹起来;数值类型的用双引号,双加号即可;
st.executeUpdate("INSERT INTO t_student(NAME,sex,birthday,phone,gra_id,res_time,score)" +
"VALUES('"+name+"','"+sex+"','"+birthday+"','"+phone+"',"+gra_id+",'"+res_time+"',"+score+")");
//关闭数据库连接通道;
st.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
使用PrepareStatement接口执行sql语句 (简单且安全性高这个接口还继承了Statement
)
首先会预先地将SQL语句编译到PrepareStatement对象中;在sql语句中参数位置使用占位符(即?
),一个占位符表示一个值;然后再向SQL语句中的占位符赋值;
在向占位符赋值时;可以使用set方法;来为各种参数赋值;
这些方法的参数parameterIndex 为int类型的,即占位符的位置(第几个 ?),
而且在传入数据时,可以直接将数据一个一个地赋值;不像Statement那样;需要注意使用符号分隔数据;
对占位符赋值结束后,还需要执行PrepareStatement接口的executeUpdate方法;
这个方法也是返回int类型的值;
若执行DDL语句则返回数字0;
若执行DML语句则返回操作的数据行数.
例如:使用PrepareStatement接口执行sql语句向数据库studentworld_db的数据表t_student中插入数据
public class Demo03 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection= DriverManager.getConnection(url,"root","123456");
//模拟向数据库输入动态数据;
//以数据库的t_student表为例;
String name="梅尔萨斯";
String sex="女";
String birthday="2014-1-6";
String phone="12342365412";
int gra_id=1805;
Date date=new Date();
java.sql.Timestamp res_time= new java.sql.Timestamp(date.getTime());
int score=75;
//向数据库发送SQL语句;这里会使用 ?(占位符);来表示一个值的位置;
String sql="INSERT INTO t_student(NAME,sex,birthday,phone,gra_id,res_time,score)VALUES(?,?,?,?,?,?,?)";
//预先将SQL语句编译到对象中;
PreparedStatement pps=connection.prepareStatement(sql);
//向占位符赋值;(注意数据类型);
pps.setString(1,name);
pps.setString(2,sex);
pps.setObject(3,birthday);
pps.setString(4,phone);
pps.setInt(5,gra_id);
pps.setTimestamp(6,res_time);
pps.setInt(7,score);
//执行方法executeUpdate;
int res=pps.executeUpdate();
System.out.println(res);
//关闭数据库连接通道;
pps.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
Statement和PreparedStatement的区别
Statement:
- 静态的SQL语句执行;每当操作一次就向数据库发送一次;
- 需要将变量以字符串的形式拼接;书写麻烦;代码的可读性以及维护性太差.
- 不可以防止SQL注入;安全性低.
PreparedStatement
- 预先地将SQL语句编译到PreparedStatement对象中;可重复使用;效率高
- 使用的是set方法为写好的占位符进行赋值;书写方便;代码的可读性以及维护性好.
- 可防止SQL注入,安全性高.
SQL注入:
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
原理:
SQL注入攻击是通过操作输入来修改SQL语句,用以达到执行代码对WEB服务器进行攻击的方法。简单的说就是在post/getweb表单、输入域名或页面请求的查询字符串中插入SQL命令,最终使web服务器执行恶意命令的过程。可以通过一个例子简单说明SQL注入攻击。假设某网站页面显示时URL为http://www.example.com?test=123,此时URL实际向服务器传递了值为123的变量test,这表明当前页面是对数据库进行动态查询的结果。由此,我们可以在URL中插入恶意的SQL语句并进行执行。另外,在网站开发过程中,开发人员使用动态字符串构造SQL语句,用来创建所需的应用,这种情况下SQL语句在程序的执行过程中被动态的构造使用,可以根据不同的条件产生不同的SQL语句,比如需要根据不同的要求来查询数据库中的字段。这样的开发过程其实为SQL注入攻击留下了很多的可乘之机。
模拟SQL注入:
(1)使用Statement不能防止SQL注入:
例如想要通过危险的SQL语句条件删除数据表t_fortnitepe中的person角色"战术咸鱼"那一行;但是整个数据表都被删除数据了.
public class Demo04 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection= DriverManager.getConnection(url,"root","123456");
//模拟暴力的SQL注入,直接将数据值拼接到字符串中,将1==1 也当做person值了;
String person="'战术咸鱼' or 1=1";
//向数据库发送数据;
Statement st=connection.createStatement();
int res= st.executeUpdate("DELETE FROM t_fortnitepe WHERE person="+person);
System.out.println(res);//获取被删除的行数;
//关闭数据库连接通道;
st.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
发现删除了10行数据,数据表被清空了.
把数据表的数据重新备份回去;
(2)PreparedStatement可以防止SQL注入:
例如想要通过带有危险的SQL语句条件删除数据表t_fortnitepe中的person角色"战术咸鱼"那一行;数据表的任何数据不会被删除.
在数据赋值给占位符时会进行预先处理;也就不会将危险的数据执行了;
public class Demo05 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection= DriverManager.getConnection(url,"root","123456");
//定义删除的条件;
String person="'战术咸鱼' or 1=1";
//预先将SQL语句编译到对象中;这里会使用 ?(占位符);来表示一个值的位置;
PreparedStatement pps=connection.prepareStatement("DELETE FROM t_fortnitepe WHERE person=?");
//向占位符赋值;
pps.setString(1,person);
//执行方法executeUpdate;
int res=pps.executeUpdate();
System.out.println(res);//获取被删除的行数;
//关闭数据库连接通道;
pps.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
发现删除0行数据;数据表的数据都在
对于结果集的处理
注意使用方法executeQuery执行查询语句;
返回一个ResultSet 集合
ResultSet res=executeQuery( );
可以将ResultSet对象获取到;然后转化到Java中自定义的类的对象;一个对象就可以存储一行数据记录.若有多行结果集数据,可以将所有对象存到一个集合中.
- ResultSet中的next( )方法;可以判断查询出的结果集中是否还有数据;如果有就返回true;指向下一行数据.
- ResultSet中的get***( )方法可以获取到查询的结果集的数据.
例如,查询1801班的学生;
public class Demo06 {
public static void main(String[] args) {
//建立与数据库的连接;
String url="jdbc:mysql://127.0.0.1:3306/studentworld_db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
try {
//与数据库连接的通道;
Connection connection= DriverManager.getConnection(url,"root","123456");
//定义查询的条件;
int gra_id=1801;
//这里会使用 ?(占位符);来表示一个值的位置;
String sql="SELECT stu_id,name,sex,phone,gra_id,res_time,score FROM t_student WHERE gra_id=?";
//预先将SQL语句编译到对象中;
PreparedStatement pps=connection.prepareStatement(sql);
//向占位符赋值;
pps.setInt(1,gra_id);
//使用方法executeQuery执行查询语句;
ResultSet res=pps.executeQuery();
//可以将数据表的数据装到自定义的集合中;
List<Student> studentList=new ArrayList<>();
//使用next方法判断结果集中是否还有数据;有就返回true,继续查下一行数据;
while(res.next()){
//每查询到一行数据就新建一个对象;
Student student=new Student();
//使用get方法获取结果集中的数据,然后存入Student类的对象中;
student.setStuID(res.getInt("stu_id"));
student.setName(res.getString("name"));
student.setSex(res.getString("sex"));
student.setPhone(res.getString("phone"));
student.setGraID(res.getInt("gra_id"));
student.setResTime(res.getTimestamp("res_time"));
student.setScore(res.getInt("score"));
//将对象中的数据存入集合;
studentList.add(student);
}
//遍历输出集合studentlist;
for(Student list:studentList){
System.out.println(list);
}
//关闭数据库连接通道;
res.close();
pps.close();
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
自定义的学生类;
public class Student {
//属性;
private int stuID;
private String name;
private String sex;
private String phone;
private int graID;
private Date resTime;
private int score;
//get,set方法;
public int getStuID() {
return stuID;
}
public void setStuID(int stuID) {
this.stuID = stuID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public int getGraID() {
return graID;
}
public void setGraID(int graID) {
this.graID = graID;
}
public Date getResTime() {
return resTime;
}
public void setResTime(Date resTime) {
this.resTime = resTime;
}
public int getScore() {
return score;
}
public void setScore(int score) {
JDBC基础--[JDBC概述,JDBC的搭建,PreparedStatement和Statement执行SQL语句,结果集处理]
Java_JDBC使用Statement和PreparedStatement
PreparedStatement 比使用 JDBC 的 Statement 慢
JDBC中的Statement和PreparedStatement的区别