在前面使用的Statement中,编写sql语句使用的是拼接的形式,这样不仅可读性差,还非常容易导致出错,最大的问题是安全问题。
sql注入
在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们DAO中写的SQL语句合成一个完整的SQL语句!例如用户在登录时输入的用户名和密码都是为SQL语句的片段!
我准备了一张user表,上面有username和password用来模拟用户表,接下来准备了代码模拟登录过程
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/esa?useSSL=false", "root", "123456");
System.out.println("数据库连接成功");
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String pwd = sc.nextLine();
String sql = "select count(1) from user where username = '"+name+"' and password = '"+pwd+"' ";
Statement statement = conn.createStatement();
ResultSet rs = statement.executeQuery(sql);
rs.next();
int count = rs.getInt(1);
if (count > 0){
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
if (statement != null){
statement.close();
}
if (conn != null){
conn.close();
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
通常情况下登录正常。
但是!
由于我们是拼接的sql语句,导致可以拼接上or '1' = '1'的语句,这样无论输入的账户密码是什么都会判断true,这种拼接的sql语句非常危险!
PreparedStatement
PreparedStatement是Statement的子接口,可以使用PreparedStatement来替换Statement。
PreparedStatement的好处:
防止SQL攻击;
提高代码的可读性,以可维护性;
提高效率。
PreparedStatement的使用
使用Connection的prepareStatement(String sql):即创建它时就让它与一条SQL模板绑定;
调用PreparedStatement的setXXX()系列方法为问号设置值
调用executeUpdate()或executeQuery()方法,但要注意,调用没有参数的方法;
此时sql语句用问号占位,写成:
String sql = "select count(1) from user where uswrname = ? and password = ?";
还要传入参数填充占位,索引从1开始,如果不知道数据类型可以实现setObject:
stat.setString(1,name);
stat.setString(2,pwd);
Scanner sc = new Scanner(System.in);
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/esa?useSSL=false", "root", "123456");
System.out.println("数据库连接成功");
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String pwd = sc.nextLine();
String sql = "select count(1) from user where uswrname = ? and password = ?";
PreparedStatement stat = conn.prepareStatement(sql);
stat.setString(1,name);
stat.setString(2,pwd);
ResultSet rs = stat.executeQuery();
rs.next();
int count = rs.getInt(1);
if (count > 0){
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
if (stat != null){
stat.close();
}
if (conn != null){
conn.close();
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SQLException e) {
throw new RuntimeException(e);
}
这样就可以防止sql注入
PreparedStatement最大的好处就是在于重复使用同一模板,给予其不同的参数来重复的使用它。这才是真正提高效率的原因。