JDBC_04Statement和PreparedStatement
1.对比Statement和PreparedStatement
1.Statement存在sql注入问题,PreparedStatement解决了sql注入问题。
2.PreparedStatement效率比Statement高一些
在mysql中,执行一条语句需要编译运行。
而如果执行过一条语句后,下一条语句跟前一条语句一模一样,就不会编译,直接运行。
使用Statement时,每次执行都要重新拼接sql语句,每次都需要重新编译运行:
//使用Statement
String sql = "select * from t_user where loginName = '"+ userInfo.get("loginName") +"' and loginPwd = '"+ userInfo.get("loginPwd") +"'";
rs = stmt.executeQuery(sql);
而使用PreparedStatement时,sql语句是固定的,在编译一次后,以后都不需要再次编译,直接运行:
//使用PreparedStatement
String sql = "select * from t_user where loginName = ? and loginPwd = ?";
ps = conn.prepareStatement(sql);
Statement是编译一次执行一次。PreparedStatement是编译一次执行,执行多次。
故PreparedStatement执行效率比Statement高。
3.PreparedStatement会在编译阶段做类型的安全检查
如果使用Statement,在给sql语句传值时,是将值直接拼接在sql语句字符串里的。
如果我需要传一个double类型的密码,那么我实际上传入一个String类型的密码也不会报错。
//使用Statement
String sql = "select * from t_user where loginName = '"+ userInfo.get("loginName") +"' and loginPwd = '"+ userInfo.get("loginPwd") +"'";
如果使用Statement,在给sql语句传值时,是通过setString或setDouble…方法将值传入占位符里的。
如果我需要传一个double类型的密码,那么我就需要调用setDouble方法来传值。
假如我传入一个String类型的密码,那么setDouble方法就会报错。
//PreparedStatement传值
ps.setString(1,loginName);
ps.setString(2,loginPwd);
综上,PreparedStatement使用较多,只有极少数情况下需要使用Statement。
2.必须使用Statement的情况
Statement支持SQL注入,在业务方面要求必须支持SQL注入的时候,必须使用Statement。
- 凡是业务方面要求是传值的,就使用PreparedStatement。
- 凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement。
就比如在执行排序操作时,就必须使用sql语句拼接。
因为在执行排序操作时,必须要指定是asc还是desc,并且asc和desc不能被单引号括住。
而如果使用PreparedStatement,就需要使用占位符,然后将asc或者desc放进字符串里,通过setString方法传值进去。
占位符会将传进来的字符串自动转义,给字符串加上单引号,这在sql语句中是不合法的。
//PreparedStatement
private static void orderBy(String orderType) {
//3.获取预编译的数据库操作对象
String sql = "select sal from emp order by sal ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1,orderType);
//select sal from emp order by sal 'desc'
//4.执行SQL语句
rs = ps.executeQuery();
}
所以必须使用sql语句拼接,必须使用Statement。
//Statement
private static void orderBy(String orderType) {
//3.获取数据库操作对象
Statement stmt = conn.createStatement();
//4.执行sql语句
String sql = "select sal from emp order by sal "+ orderType;
//select sal from emp order by sal desc
rs = stmt.executeQuery(sql);
}
完整排序代码:
package com.tsccg.jdbc.statement;
import java.sql.*;
import java.util.Scanner;
/**
* @Author: TSCCG
* @Date: 2021/07/28 16:39
*/
public class OrderStatementDemo01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("排序方式:");
String orderType = sc.nextLine();
orderBy(orderType);
}
/**
* 将数据库中的数据排序显示出来
*/
private static void orderBy(String orderType) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
String url = "jdbc:mysql://127.0.0.1:3306/tsccg";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url,user,password);
//3.获取数据库操作对象
stmt = conn.createStatement();
//4.执行sql语句
String sql = "select sal from emp order by sal "+ orderType ;
rs = stmt.executeQuery(sql);
//5.处理查询结果集
while (rs.next()) {
System.out.println(rs.getString("sal"));
}
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
//6.释放资源
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
运行:
排序方式:asc
0.00
0.00
0.00
800.00
950.00
1100.00
1250.00
1250.00
1500.00
1600.00
2850.00
2975.00
3000.00
3000.00
排序方式:desc
3000.00
3000.00
2975.00
2850.00
1600.00
1500.00
1250.00
1250.00
1100.00
950.00
800.00
0.00
0.00
0.00
3.PreparedStatement实现增删改
3.1增:向t_user表插入数据
package com.tsccg.jdbc.statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;
/**
* @Author: TSCCG
* @Date: 2021/07/28 17:25
* 使用PreparedStatement实现增删改
*/
public class PreparedStatementDemo01 {
public static void main(String[] args) {
boolean result = change();
System.out.println(result ? "操作成功" : "操作失败");
}
/**
* 增删改t_user表中的数据
* @return 返回执行结果
*/
private static boolean change() {
boolean isSuccess = false;
Connection conn = null;
PreparedStatement ps = null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
String url = "jdbc:mysql://localhost:3306/tsccg";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url,user,password);
//3.获取预编译数据库操作对象
String sql = "insert into t_user(loginName,loginPwd,realName) values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1,"zhaoliu");
ps.setString(2,"123456");
ps.setString(3,"赵六");
//4.执行sql语句
int count = ps.executeUpdate();
if (count > 0) {
isSuccess = true;
}
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
//6.释放资源
if (ps != null) {
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return isSuccess;
}
}
运行:
操作成功
3.2改:修改t_user里的数据
将上面程序中第3步的语句修改为:
//3.获取预编译数据库操作对象
String sql = "update t_user set loginName = ?,realName = ? where id = ?";
ps = conn.prepareStatement(sql);
ps.setString(1,"wangwu");
ps.setString(2,"王五");
ps.setInt(3,3);
重新运行程序:
操作成功
3.3删:删除t_user中的数据
将上面程序中第3步的语句修改为:
//3.获取预编译数据库操作对象
String sql = "delete from t_user where id = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1,3);
重新运行程序:
操作成功