JDBC预编译学习笔记
使用JDBC中的statement对象执行SQL语句时,由于SQL语句可以通过字符串进行拼接,因此会出现SQL注入问题,是不安全的。
例如:
//定义SQL
String sql = "select * from user where userName='"+userName+"' and password="+password;
输入的用户名与密码匹配才能获得用户的信息,但是如果把[or 1 = 1]作为password传入进来,就成了
String sql = "select * from user where userName='随意用户名' and password='' or 1=1";
因为 1=1 成立,where条件一定为真,所以密码错误也可以通过验证。
使用PreparedStatement
预编译语句PreparedStatement 是java.sql中的一个接口,它是Statement的子接口。使用PreparedStatement的参数化的查询可以阻止大部分的SQL注入。
预编译的使用:
1.在定义sql语句的时候,待填入的参数用 ? 占位。例如下面的代码
//定义SQL
String sql = "select * from student where sname=? and sid=?";
2.创建 PreparedStatement 预编译对象 ,并把sql语句传入。
//预编译sql,conn是Connection对象
PreparedStatement ps = conn.prepareStatement(sql);
3.给占位符 ? 设置参数,方法的参数一代表占位符的位置(从1开始),参数二代表要设置的值,使用的setXX()方法与数值类型有关,字符串就是setString(index,str).
ps.setString(1, "张三");
ps.setInt(2, 9);
// sql语句设置成了 "select * from student where sname='张三' and sid=9";
4.执行sql语句,第二步已经把sql语句与preparedStatement绑定,所以此时无需再把sql语句作为参数传入executeQuery。
//执行SQL获取结果集
ResultSet rs = ps.executeQuery();
一旦设置了给定语句的参数值,其值将一直保留,直到被设置为新值或者调用clearParameters()方法清除它为止。
在使用参数化查询的情况下,数据库系统不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有破坏性的指令,也不会被数据库所运行。
预编译增删改封装
/**
* 数据库连接工具类
*/
public class DBUtil {
//数据库驱动
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
//数据库连接参数,staff_manager是数据库名称
private static final String URL = "jdbc:mysql://localhost:3306/staff_manager?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai";
//登录数据库的用户名
private static final String USER = "root";
//数据库登录密码
private static final String PASSWORD = "123456";
private static Connection conn =null;
//注册驱动
static {
try {
//反射
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接对象
*/
public static Connection newInstance() {
try {
conn = DriverManager.getConnection(URL, USER, PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 使用预编译对象的增删改通用方法
* @param sql 预编译sql语句
* @param objects 给占位符设置参数
* @return
*/
public static int precurdMethod(String sql,Object...objects) {
//PreparedStatement类有预编译sql语句的方法,变量定义在上面供下面各个代码块使用
PreparedStatement ps = null;
//创建连接
newInstance();
//创建预编译对象,并连接sql语句
try {
ps = conn.prepareStatement(sql);
//先判断sql语句是否有占位符要设置
if(objects != null) {
//第二个参数不为null,则说明需要设置sql语句的占位符
for(int i=0;i<objects.length;i++) {
//每次循环就给一个占位符 ? 设置参数,注意占位符下标是从1开始
ps.setObject(i+1, objects[i]);
}
}
//执行设置完占位符后的sql,返回值是sql语句影响到数据库的行数
int result = ps.executeUpdate();
return result;
} catch (SQLException e) {
e.printStackTrace();
}finally {
//关闭连接
close(conn,ps,null);
}
return 0;
}
/**
* 关闭连接对象
* @param conn 连接数据库对象
* @param sta 预编译sql语句对象
* @param rs 结果集对象
*/
public static void close(Connection conn,Statement sta,ResultSet rs) {
try {
if(rs != null){
rs.close();
}
if(sta != null) {
sta.close();
}
if(conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}