JDBC API详解——PreparedStatement
PreparedStatement是一个接口,继承自Statement,表示预编译SQL语句的对象。
作用:
1.预编译SQL语句,性能更高;
2.预防SQL注入问题,将敏感字符进行转义
- SQL注入:SQL注入是通过操作输入来修改事先定义好的SQL语句。用以达到执行代码对服务器进行攻击的方法。
以下是一个简单的登录代码:
//接受用户输入的用户名和密码
String input_name = "Mike";
String input_pwd = "123";
//3、定义sql
String sql = "select * from login_user where Login_name = '"+input_name+"' and Login_pwd = '"+input_pwd+"'";
//4、获取执行sql的对象Statement
Statement stmt = conn.createStatement();
//5、执行sql
ResultSet rs = stmt.executeQuery(sql);
//6、处理结果,判断登录是否成功,只要rs里面有数据,就说明登录成功
if(rs.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
因为在数据库的表中能查到用户输入的登录名和密码,所以运行显示登录成功。
如果用户名随便写,密码也随便写,如下:
String input_name = “1vshupasse”;
String input_pwd = “’ or ‘1’='1”;
仍然会显示执行成功。
打印一下sql,select * from login_user where Login_name = ‘1vshupasse’ and Login_pwd = ’ ’ or ‘1’=‘1’,修改了SQL原来的含义,or前面返回false,但or后面是恒等式,所以where后面的语句就没有起到作用,就会把所有的数据全部查出来。这就是SQL注入。
为了解决SQL注入问题,可以使用PreparedStatement对象解决,有以下步骤:
(1)获取PreparedStatement对象
- //定义sql,SQL语句中的参数值,使用?占位符替代,避免了拼字符串:String sql = “select * from login_user where Login_name = ? and Login_pwd = ?”;
- //通过Connection获取pstmt对象,并传入对应的sql语句:
PreparedStatement pstmt = conn.prepareStatement(sql);
(2)设置参数值
PreparedStatement对象:setXxx(参数1,参数2),给占位符?赋值
- Xxx:数据类型,如setInt(参数1,参数2),要跟表中属性的数据类型对应
- 参数:参数1:?的位置编号,从1开始;
参数2:?的值
例如:
//设置?的值
pstmt.setString(1,input_name);
pstmt.setString(2,input_pwd);
(3)执行SQL
executeUpdate(); 或者executeQuery(); 此时括号内不需要再传递sql。
以下是完整代码:
@Test
public void testPreparedStatement() throws Exception {
//1、创建驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、获取连接
String url = "jdbc:mysql://localhost:3306/db1?serverTimezone=GMT";
String uname = "root";
String pwd = "123456";
Connection conn = DriverManager.getConnection(url,uname,pwd);
//接受用户输入的用户名和密码
String input_name = "Mike";
String input_pwd = "1234";
//3、定义sql,SQL语句中的参数值,使用?占位符替代,避免了拼字符串
String sql = "select * from login_user where Login_name = ? and Login_pwd = ?";
//通过Connection获取pstmt对象,并传入对应的sql语句
PreparedStatement pstmt = conn.prepareStatement(sql);
//设置?的值
pstmt.setString(1,input_name);
pstmt.setString(2,input_pwd);
//5、执行sql
ResultSet rs = pstmt.executeQuery();
//6、处理结果,判断登录是否成功,只要rs里面有数据,就说明登录成功
if(rs.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//7、释放资源
rs.close();
pstmt.close();
conn.close();
}
PreparedStatement原理
(1)PreparedStatement预编译功能默认是关闭的,开启: &useServerPrepStmts=true
(2)配置MySQL执行日志(重启mysql服务后生效)
log-output=FILE
general-log=1
general_log_file="D:\mysql.log"
slow-query-log=1
slow_query_log_file="D:\mysql_slow.log"
long_query_time=2
- 将上述代码复制到mysql安装路径的my.ini文件下,重启mysql,在IDEA中开启预编译功能,执行后,可在log文件中看到整个执行的过程
(3)原理
- 在获取PreparedStatement对象时,将sql语句发送给mysql服务器进行检查编译
- 执行时就不用在进行以上步骤,速度更快
- 如果sql模板一样,则只需要进行一次检查、编译。