Statement和PreparedStatement的区别

预处理语句是Statement的一个稍微强大一点的版本,并且应该总是至少像Statement一样快速和容易处理。< br / > 预准备报表可以参数化

大多数关系数据库处理JDBC / SQL查询分为四个步骤:

  1. 解析传入的SQL查询
  2. 编译SQL查询
  3. 规划/优化数据采集路径
  4. 执行优化的查询/获取和返回数据

对于发送到数据库的每个SQL查询,Statement将始终执行上述四个步骤。预处理语句预先执行上述执行过程中的步骤(1)-(3)。因此,在创建准备语句时,会立即执行一些预优化。其效果是在执行时减轻数据库引擎的负载。

现在我的问题是:

“使用预准备报表还有其他好处吗?”

283845 次浏览

PreparedStatement是防止SQL注入攻击的一个非常好的防御(但不是万无一失的)。绑定参数值是防止“小鲍比桌子”进行不必要的访问的好方法。

没什么可补充的,

1 -如果你想在循环中执行查询(超过1次),由于你提到的优化,预处理语句可以更快。

2 .参数化查询是避免SQL注入的好方法。参数化查询只在PreparedStatement中可用。

PreparedStatement的优点:

  • SQL语句的预编译和db端缓存使得整体执行速度更快,并且能够在中重用相同的SQL语句。

  • 通过内置转义引号和其他特殊字符自动防止SQL注入 攻击。注意,这要求你使用任何PreparedStatement setXxx()方法来设置值

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
    preparedStatement.setString(1, person.getName());
    preparedStatement.setString(2, person.getEmail());
    preparedStatement.setTimestamp(3, new Timestamp(person.getBirthdate().getTime()));
    preparedStatement.setBinaryStream(4, person.getPhoto());
    preparedStatement.executeUpdate();
    

    因此通过串连接内联SQL字符串中的值。

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email) VALUES ('" + person.getName() + "', '" + person.getEmail() + "'");
    preparedStatement.executeUpdate();
    
  • Eases setting of non-standard Java objects in a SQL string, e.g. Date, Time, Timestamp, BigDecimal, InputStream (Blob) and Reader (Clob). On most of those types you can't "just" do a toString() as you would do in a simple Statement. You could even refactor it all to using PreparedStatement#setObject() inside a loop as demonstrated in the utility method below:

    public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
    for (int i = 0; i < values.length; i++) {
    preparedStatement.setObject(i + 1, values[i]);
    }
    }
    

    可使用如下:

    preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
    setValues(preparedStatement, person.getName(), person.getEmail(), new Timestamp(person.getBirthdate().getTime()), person.getPhoto());
    preparedStatement.executeUpdate();
    

不能在语句中使用clob。

And: (OraclePreparedStatement) ps

  • 这样更容易阅读
  • 您可以轻松地将查询字符串设置为常量

准备语句忽略SQL注入,提高了准备语句的安全性

Statement接口执行不带参数的静态SQL语句

PreparedStatement interface (extends Statement)执行预编译的带/不带参数的SQL语句

  1. 对于重复执行有效

  2. 它是预编译的,所以更快

  1. 它们是预编译的(一次),因此可以更快地重复执行动态SQL(当参数更改时)

  2. 数据库语句缓存提高了数据库执行性能

    数据库存储以前执行语句的执行计划的缓存。这允许数据库引擎重用以前执行过的语句的计划。由于PreparedStatement使用参数,每次执行时都显示为相同的SQL,数据库可以重用先前的访问计划,从而减少处理。语句将参数“内联”到SQL字符串中,因此不会作为相同的SQL出现在DB中,从而防止缓存的使用

  3. 二进制通信协议意味着更少的带宽和更快的对DB服务器的通信调用

    准备语句通常通过非sql二进制协议执行。这意味着数据包中的数据更少,因此与服务器的通信更快。根据经验,网络操作比磁盘操作慢一个数量级,而磁盘操作又比内存中的CPU操作慢一个数量级。因此,通过网络发送的数据量的任何减少都将对整体性能产生良好的影响

  4. 它们通过转义所提供的所有参数值的文本来防止SQL注入。

  5. 它们在查询代码和参数值之间提供了更强的分离(与连接的SQL字符串相比),提高了可读性,并帮助代码维护者快速理解查询的输入和输出。

  6. 在java中,可以调用getMetadata()和getParameterMetadata()分别反映结果集字段和参数字段

  7. 在java中,智能地接受java对象作为参数类型通过setObject, setBoolean, setByte, setDate, setDouble, setDouble, setFloat, setInt, setLong, setShort, setTime, setTimestamp -它转换为JDBC类型格式是可理解的DB(不仅仅是toString()格式)。

  8. 在java中,通过setArray方法接受SQL数组作为参数类型

  9. 在java中,分别通过setClob/setNClob, setBlob, setBinaryStream, setCharacterStream/setAsciiStream/setNCharacterStream方法接受clob, blob, OutputStreams和Readers作为参数“feed”

  10. 在java中,允许通过setURL, setRowId, setSQLXML ans setNull方法为SQL DATALINK, SQL ROWID, SQLXML和NULL设置特定于db的值

  11. 在java中,从Statement中继承所有方法。它继承了addBatch方法,并且允许通过addBatch方法添加一组参数值来匹配批处理SQL命令集。

  12. 在java中,一种特殊类型的PreparedStatement(子类CallableStatement)允许执行存储过程——支持高性能、封装、过程性编程和SQL、数据库管理/维护/调整逻辑,以及使用专有的DB逻辑&特性

语句将用于执行静态SQL语句,它不能接受输入参数。

PreparedStatement将用于多次动态执行SQL语句。它将接受输入参数。

语句是静态的,准备语句是动态的。

Statement适用于DDL, prepared Statement适用于DML。

语句较慢,而准备语句较快。

更多的差异(存档)

PreparedStatement相对于Statement的一些好处是:

  1. PreparedStatement可以帮助我们防止SQL注入攻击,因为它会自动转义特殊字符。
  2. PreparedStatement允许我们使用参数输入执行动态查询。
  3. PreparedStatement提供了不同类型的setter方法来设置查询的输入参数。
  4. PreparedStatement比Statement快。当我们重用PreparedStatement或使用它的批处理方法执行多个查询时,它变得更加明显。
  5. PreparedStatement帮助我们使用setter方法编写面向对象的代码,而对于Statement,我们必须使用String concatation来创建查询。如果要设置多个参数,则使用字符串连接编写Query看起来非常难看,而且容易出错。

阅读更多关于SQL注入问题在http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example

不要混淆:只要记住

  1. Statement用于静态查询,如ddl,即create,drop,alter和prepareStatement用于动态查询,即DML查询。
  2. 在Statement中,查询不被预编译,而在prepareStatement中查询被预编译,因为这样的prepareStatement是时间高效的。
  3. prepareStatement在创建时接受参数,而Statement不接受参数。 例如,如果你想创建表并插入元素,那么:: 使用Statement创建表(静态),使用prepareStatement插入元素(动态)

准备或参数化查询的另一个特征:引用自这篇文章。

该语句是数据库系统重复执行同一条SQL语句的高效特性之一。准备好的语句是模板的一种,由应用程序使用不同的参数。

语句模板准备好后发送给数据库系统,数据库系统对该模板进行解析、编译和优化,不执行即可存储。

有些参数,比如在以后的应用程序创建模板时没有传递子句,将这些参数发送给数据库系统,数据库系统使用SQL语句模板并按请求执行。

预处理语句对于SQL注入非常有用,因为应用程序可以使用不同的技术和协议来准备参数。

当数据数量不断增加,索引频繁变化时,Prepared statement可能会失败,因为在这种情况下需要一个新的查询计划。

As cited by mattjames

在JDBC中使用的语句应该100%本地化为正在使用 对于DDL (ALTER, CREATE, GRANT等),因为这些是唯一的语句 不能接受BIND变量的类型。preparedstatement或 CallableStatements应该用于所有其他类型的语句 (DML、查询)。因为这些是接受bind的语句类型 变量。< / p > 这是一个事实,一条规则,一条法则——在任何地方都使用准备好的语句。

.

.

我遵循这个问题的所有答案,使用- Statement(但有SQL注入)将工作遗留代码更改为使用PreparedStatement的解决方案,其代码要慢得多,因为对Statement.addBatch(String sql) &的语义理解不好;PreparedStatement.addBatch()

所以我在这里列出了我的情况,这样其他人就不会犯同样的错误。

我的设想是

Statement statement = connection.createStatement();


for (Object object : objectList) {
//Create a query which would be different for each object
// Add this query to statement for batch using - statement.addBatch(query);
}
statement.executeBatch();

所以在上面的代码中,我有数千个不同的查询,都添加到相同的语句中,这段代码工作得更快,因为语句不被缓存是好的&这段代码很少在应用程序中执行。

现在修复SQL注入,我把这段代码改为,

List<PreparedStatement> pStatements = new ArrayList<>();
for (Object object : objectList) {
//Create a query which would be different for each object
PreparedStatement pStatement =connection.prepareStatement(query);
// This query can't be added to batch because its a different query so I used list.
//Set parameter to pStatement using object
pStatements.add(pStatement);
}// Object loop
// In place of statement.executeBatch(); , I had to loop around the list & execute each update separately
for (PreparedStatement ps : pStatements) {
ps.executeUpdate();
}

所以你看,我开始创建成千上万的PreparedStatement对象&;然后最终不能利用批处理,因为我的场景要求- 有成千上万的更新或插入查询&所有这些查询都是不同的。

修复SQL注入是强制性的,不会降低性能,我认为在这种情况下使用PreparedStatement是不可能的。

另外,当你使用内置批处理工具时,你必须担心只关闭一个语句,但使用这个List方法,你需要在重用之前关闭语句重用PreparedStatement