前面文章中我们了解到真正执行 sql 语句的是 Statement 对象。它执行相应的方法就能够操作数据库。这边给他传递的 sql 语句都是瓶装好的语句。它其实是不安全的,如果别有用心的人可以在 sql 语句上面动点手脚,就会导致系统有问题。
预处理就和现实生活中的占座的道理是一样的,如果你和嗨客礼拜天约定一起去图书馆看书,你先到了图书管,嗨客临时有事要半个小时没来,这个时候,你就会帮嗨客占一个位置,不管他有没有到,这个位置就是它的了。
Java 语言里面就定义了一个 PreparedStatement 接口来处理预处理。它是 Statement 的子接口,所以调用方式和 Statement 差不多。
PreparedStatement 对象已经预编译过,它的执行速度会比 Statement 对象快。在 PreparedStatement 对象中它对具体的内容用 “?” 来占位,然后设置内容的时候,按照 “?” 的内容来对数据进行赋值。
方法名 | 描述 |
---|---|
int executeUpdate() throws SQLException | 执行设置的预处理 SQL 语句 |
ResultSet executeQuery() throws SQLException; | 执行数据库查询操作 |
void setInt(int parameterIndex, int x) throws SQLException; | 在指定的索引的位置赋值,赋值类型是 int 类型 |
void setFloat(int parameterIndex, float x) throws SQLException; | 在指定的索引的位置赋值,赋值类型是 float 类型 |
void setString(int parameterIndex, String x) throws SQLException; | 在指定的索引位置赋值,赋值类型是 String 类型。 |
其实 基本的数据类型 这边都可以设置,设置方式和上面的表格类型,但是有一个特殊的类型是 Date 类型。我们平时代码里面用的是 java.util.Date 类型,但是 mysql 这边操作的时候需要用 java.sql.Date 类型。
我们使用 sql 预处理的方式进行插入数据和查询数据,查询我们可以试着模糊查询。
import java.sql.*;
public class MysqlTest {
public static final String DBDRIVER = "com.mysql.jdbc.Driver";
public static final String DBURL = "jdbc:mysql://localhost:3306/haicoder";
public static final String DBUSER = "root";
public static final String PASSWORD = "123456";
public static void main(String[] args) throws Exception {
System.out.println("嗨客网(www.haicoder.net)");
Connection connection = null;
//加载驱动
Class.forName(DBDRIVER);
//创建连接
connection = DriverManager.getConnection(DBURL, DBUSER, PASSWORD);
String sql = "INSERT INTO person (username,age) values (?,?)";
//创建 statement 对象
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, "嗨客网");
statement.setInt(2, 100);
//执行sql
statement.executeUpdate();
statement.setString(1, "IT 社区");
statement.setInt(2, 100);
statement.executeUpdate();
String querySql = "select username,age from person where username like ?";
PreparedStatement queryStatement = connection.prepareStatement(querySql);
queryStatement.setString(1, "%嗨客%");
ResultSet resultSet = queryStatement.executeQuery();
while (resultSet.next()) {
String name = resultSet.getString("username");
Integer age = resultSet.getInt("age");
System.out.println("name in db is :" + name + " age in db is :" + age);
}
//关闭操作
statement.close();
queryStatement.close();
//关闭数据库连接
connection.close();
System.out.println("结束");
}
}
运行结果如下:
我们插入了两条记录,然后用模糊搜索,查询出名字里面包含 嗨客
的数据。
sql 注入的意思是通过传递一些参数,让我们的 sql 执行改变意志。比如我们模糊查询 person 表,按照名字来模糊查询数据。我们使用预处理方式查询和直接查询。
import java.sql.*;
public class MysqlTest {
public static final String DBDRIVER = "com.mysql.jdbc.Driver";
public static final String DBURL = "jdbc:mysql://localhost:8080/haicoder";
public static final String DBUSER = "root";
public static final String PASSWORD = "123456";
public static void main(String[] args) throws Exception {
System.out.println("嗨客网(www.haicoder.net)");
Connection connection = null;
//加载驱动
Class.forName(DBDRIVER);
System.out.println("=======sql 预处理 开始=======");
//创建连接
connection = DriverManager.getConnection(DBURL, DBUSER, PASSWORD);
String querySql = "select username,age from person where username like ?";
PreparedStatement queryStatement = connection.prepareStatement(querySql);
queryStatement.setString(1, "'%嗨客%' OR 1=1");
ResultSet resultSet = queryStatement.executeQuery();
while (resultSet.next()) {
String name = resultSet.getString("username");
Integer age = resultSet.getInt("age");
System.out.println("name in db is :" + name + " age in db is :" + age);
}
System.out.println("=======sql 预处理 结束=======");
System.out.println("=======sql 注入 开始=======");
//创建 statement 对象
Statement statement = connection.createStatement();
String statementSql = "select * from person where username like '%嗨客%' OR 1=1";
//执行sql
ResultSet statementResultSet = statement.executeQuery(statementSql);
while (statementResultSet.next()) {
String name = statementResultSet.getString("username");
Integer age = statementResultSet.getInt("age");
System.out.println("name in db is :" + name + " age in db is :" + age);
}
System.out.println("=======sql 注入 结束=======");
//关闭操作
queryStatement.close();
statement.close();
//关闭数据库连接
connection.close();
System.out.println("结束");
}
}
运行结果如下
我们可以看到,使用 Statement 来查询 sql 的时候,在 条件里面 加了一个 or 1=1 ,sql 语句就变了,所以它将数据库里面的所有语句都查询了出来,而使用预处理的时候,它认为是将 username 是 ‘%嗨客%’ OR 1=1 查询出来,从而没有记录。
在真实的生产环境中,我们对数据库操作使用 PreparedStatement 比较多,它是安全的,防止 sql 依赖注入的。而且 PreparedStatement 效率比 Statement 效率高。