我们啥时候应该使用 PreparedStatement 而不是 Statement?

Posted

技术标签:

【中文标题】我们啥时候应该使用 PreparedStatement 而不是 Statement?【英文标题】:When should we use a PreparedStatement instead of a Statement?我们什么时候应该使用 PreparedStatement 而不是 Statement? 【发布时间】:2011-01-07 03:37:55 【问题描述】:

我知道使用PreparedStatement的好处是

查询由数据库服务器重写和编译 防止 SQL 注入

但我想知道我们什么时候使用它而不是Statement

【问题讨论】:

PreparedStatements and performance 的可能重复项 【参考方案1】:

    查询由数据库服务器重写和编译

    如果您不使用准备好的 语句,数据库服务器将 必须解析并计算 语句的执行计划 每次运行它。如果你发现 你会运行相同的语句 多次(不同的 参数)然后它值得准备 声明一次并重用它 准备好的声明。如果你是 然后查询数据库即席 可能没有什么好处 这个。

    防止 SQL 注入

    这几乎是你的优势 总是想要一个很好的理由 每次都使用PreparedStatement。 这是不得不 参数化查询,但确实如此 使其运行更安全。这 只有一次我能想到这个 如果你是 允许临时数据库查询;你 可以简单地使用语句 对象,如果你是原型 应用程序及其更快, 或者如果查询不包含 参数。

【讨论】:

【参考方案2】:

问汤姆的opinion:

在 JDBC 中使用 Statement 应 100% 本地化为用于 DDL(ALTER、 CREATE、GRANT 等),因为这些是唯一不能接受 BIND 的语句类型 变量。

PreparedStatements 或 CallableStatements 应该用于所有其他类型的语句 (DML,查询)。因为这些是接受绑定变量的语句类型。

这是一个事实、一个规则、一个法律——在任何地方都使用准备好的语句。使用声明 几乎没有哪里。

他专门讨论的是 Oracle,但同样的原则适用于任何缓存执行计划的数据库。

同时扩展和防止 SQL 注入攻击的数据库应用程序?有什么缺点?

【讨论】:

【参考方案3】:

我会扭转这一局面:在公开分发的应用程序中,您通常应该始终使用准备好的语句除非您有真正令人信服的理由不这样做,而且您应该始终为准备好的语句“正确”提供参数,而不是通过将它们拼接到查询字符串中。

为什么?好吧,基本上是因为你给出的原因(或者至少是第二个原因)......

【讨论】:

注意:PreparedStatement 的性能可能很糟糕,除非您对它进行 大量 操作。这取决于数据库驱动程序。 谢谢,这是一个有趣的观点。出于兴趣,您是否有特定数据库/驱动程序的示例?从我对 mysql 所做的测试来看,在性能方面似乎没有任何东西。不要只记得 SQL Server,尽管不要记得准备好的语句特别糟糕。【参考方案4】:

在 WHERE 子句中应非常小心地使用 PreparedStatements。

假设一个表定义为:

create table t (int o, k varchar(100), v varchar(100))

(例如“o:object-ID(外键),k:attribute-key,v:attribute-value”)。

此外,v 上有一个(非唯一的)索引。

create index ixt on t ( v )

假设此表包含插入的 2 亿行,如下所示:

for (i = 0; i < 100*1000*1000; i++) 
  insert into t (o,k,v) values (i,'k1','v1');
  insert into t (o,k,v) values (i,'k2', Convert(i, varchar));

("因此,每个对象 o 都有属性 k1=v1 和 k2=o")

那么你不应该构建这样的查询:

select o,p,v from t as tx, t as ty where tx.o=ty.o and tx.k=? and tx.v=? and ty.k=? and ty.v=?

("查找具有两个给定属性的对象")

我对 ORACLE 和 MSSQL 的经验是,这些查询可能需要 很多分钟 才能返回。即使没有行匹配 where 子句也是如此。这取决于 SQL-Server 是决定先查找 tx.v 还是 ty.v。

应该将列 k 和 v 的值直接放入语句中。我认为这是因为 SQL-Server 在计算执行计划时会考虑这些值。

这样的查询总是在毫秒后返回:

select o,p,v from t as tx, t as ty where tx.o=ty.o and tx.k='k1' and tx.v='v1' and ty.k='k2' and ty.v='1234'

("SQL-Server 总是先搜索 v='1234' 然后再搜索 v='v1' ")

问候 沃尔夫冈

【讨论】:

【参考方案5】:

语句:每次运行 sql 查询时,都会将此 sql 语句发送到编译它的 DBMS。因此,它会增加服务器负载并降低性能。

connection con=null; 
  String sql="select * from employee where id=5";
Statement st=conn.createStatement();

PreparedStatement:与Statement不同,PreparedStatement在创建时被赋予了一个sql查询作为参数。

connection con=null; 
String sql="select * from employee where id=?";
PreparedStatement ps=conn.prepareStatement(sql);

这个 sql 语句被发送到编译它的数据库。 因此,在preparedStatement 中编译只发生一次,但在statement 中编译发生在每次调用Statement 时。

【讨论】:

【参考方案6】:

您始终可以使用 PreparedStatement 代替 Statment(选择、插入、更新、删除)。更好的性能并防止 SQL 注入。

但是,不要将它用于动态请求,例如带有 WHERE variable IN [ hundreds possibilities ] 的请求:

    这会适得其反,您会损失性能和内存,因为您每次都会缓存新请求,而 PreparedStatement 不仅用于 SQL 注入,还与性能有关。在这种情况下,Statement 不会变慢。

    您的池有 PreparedStatment 的限制(默认为 -1,但您必须限制它),您将达到此限制!如果你没有限制或非常大的限制,你会有一些内存泄漏的风险,在极端情况下会出现 OutofMemory 错误。因此,如果它是用于由 3 个用户使用的小型个人项目,它并不引人注目,但如果您在一家大公司并且您的应用程序被数千​​人和数百万请求使用,那么您不希望这样。

一些阅读。 IBM : Periodical OutOfMemory errors with prepared statement caching

【讨论】:

WHERE 变量 IN [数百种可能性] 可以传递一个数组对象(取决于您的数据库。这也往往被 Hibernate 等抽象出来) 嗨@amdev,所提供链接的页面现在不可用,最好更新。 老实说,我不知道 JDBC 如何根据数据库优化数组对象。也许它只是为幕后的每个数组创建一个新的准备好的语句。也许这取决于驱动程序和数据库。【参考方案7】:

这只是一个 Java 设计错误将“准备好的语句”与“参数化查询/绑定变量”联系在一起。

数据库确实有 API 来接受 SQL 代码中的“绑定变量”,这些代码只运行一次。

这是一个很大的资源浪费强制使用“准备语句”,只是为了防止 SQL 注入。为什么 Java 不让开发人员以正确的方式使用数据库?

可能如下:Statement Interface - 可以运行多个命令。不接受绑定变量。一次执行 SQL 命令。没有 SQL 注入保护。PreparedStatement Interface - 可以运行一个命令。接受绑定变量。 多次执行 SQL 命令。 SQL 注入保护。 (在 JAVA 中丢失!)RunOnceStatement - 可以运行一个命令。接受绑定变量。一次执行 SQL 命令。 SQL 注入保护。

例如,在 Postgres 中性能可能会更好,通过驱动程序映射到:Statement Interface - PQExec()PreparedStatement Interface - PQPrepare() / PQExecPrepare() / ... (在 JAVA 中丢失!)RunOnceStatement - PQExecParams()

在只运行一次的 SQL 代码中使用准备好的语句是一个很大的性能问题:在数据库中进行更多处理,浪费数据库内存,通过维护以后不会调用的计划。缓存计划变得如此拥挤,以至于多次执行的实际 SQL 命令可能会从缓存中删除。

【讨论】:

【参考方案8】:

除了防止 SQL 注入、格式化可移植性(您无法从 Statement 获得)之外,性能是显而易见的原因。但是,PreparedStatement 并非没有任何惩罚。例如,如果只运行一次,它通常比Statement 慢,因为有一些开销。所以一般的想法是当你多次执行相同的查询时应该使用PreparedStatement。但是,多少开销取决于数据库服务器的具体实现,所以从性能考虑,究竟何时选择PreparedStatement 而不是Statement,实际上应该基于您对特定数据库服务器的实际经验/实验。

【讨论】:

以上是关于我们啥时候应该使用 PreparedStatement 而不是 Statement?的主要内容,如果未能解决你的问题,请参考以下文章

我们啥时候应该使用 PreparedStatement 而不是 Statement?

我们啥时候应该使用 SNOWPIPE?

我们啥时候应该使用 save() 的高级参数?

我们啥时候应该使用 scala.util.DynamicVariable?

我们啥时候应该使用普通 BFS 而不是双向 BFS?

我们啥时候应该考虑使用私有或受保护?