Java 在单次执行后关闭PreparedStatement–;这是一个设计缺陷吗?

Java 在单次执行后关闭PreparedStatement–;这是一个设计缺陷吗?,java,jdbc,eda,Java,Jdbc,Eda,我调查过很多地方,听到了很多可疑的说法,从PreparedStatement应该比Statement更受欢迎,哪怕只是为了性能利益;一直以来都声称PreparedStatements应该专门用于批处理语句,而不是其他任何语句 然而,在我所关注的(主要是在线)讨论中似乎存在一个盲点。让我提出一个具体的情况 我们有一个EDA设计的带有DB连接池的应用程序。事件发生时,有些需要持久性,有些则不需要。有些是人工生成的(例如,每X分钟更新/重置一次)。 有些事件是按顺序发生和处理的,但其他类型的事件(也

我调查过很多地方,听到了很多可疑的说法,从
PreparedStatement
应该比
Statement
更受欢迎,哪怕只是为了性能利益;一直以来都声称
PreparedStatement
s应该专门用于批处理语句,而不是其他任何语句

然而,在我所关注的(主要是在线)讨论中似乎存在一个盲点。让我提出一个具体的情况


我们有一个EDA设计的带有DB连接池的应用程序。事件发生时,有些需要持久性,有些则不需要。有些是人工生成的(例如,每X分钟更新/重置一次)。 有些事件是按顺序发生和处理的,但其他类型的事件(也需要持久性)可以(也将)并发处理

除了那些人工生成的事件外,对于需要持久性的事件是如何到达的,没有任何结构

这个应用程序是在很久以前(大约2005年)设计的,支持多个DBMS。典型的事件处理程序(需要持久性):

  • 从池中获取连接
  • 准备sql语句
  • 执行准备好的语句
  • 处理结果集,如果适用,请将其关闭
  • 精心准备的声明
  • 如有必要,准备不同的陈述,并以相同的方式处理
  • 返回到池的连接
如果事件需要批处理,则该语句只准备一次,并使用
addBatch
/
executeBatch
方法。这是一个明显的性能优势,这些案例与此问题无关


最近,我收到一个意见,即准备(解析)一条语句,执行一次并关闭它的整个想法本质上是对
PreparedStatement
的滥用,无论使用的是服务器还是客户端准备的语句,以及典型的DBMS,都不会带来任何性能好处(Oracle、DB2、MSSQL、MySQL、Derby等)甚至不会将这样的语句提升到准备好的语句缓存中(或者至少,它们的默认JDBC驱动程序/数据源不会)。

此外,我必须在MySQL上的开发环境中测试某些场景,Connector/J usage analyzer似乎同意这种想法

PreparedStatement已创建,但使用了1次或更少的次数。一次准备语句并多次重复使用更为有效


由于前面概述的应用程序设计选择,使用
PreparedStatement
实例缓存来保存连接池中每个连接的任何事件使用的每个SQL语句听起来是一个糟糕的选择

有人能对此做进一步的阐述吗?是否“准备执行(一次)-关闭”的逻辑有缺陷且基本上不受欢迎


p.S.明确指定连接器/J的
useUsageAdvisor=true
cachePrepStmts=true
,并使用
useserverprestmts=true
useserverprestmts=false
在调用
close()时仍会导致有关效率的警告
PreparedStatement
上为每个非批处理SQL语句创建实例。

PreparedStatements
更可取,因为无论您是否以编程方式创建一个语句,都需要一个;在内部,数据库每次运行查询时都会创建一个实例-以编程方式创建一个语句只会给您一个句柄。每次创建和丢弃
PreparedStatement
都不会比使用
语句增加太多开销

创建一个数据库需要花费大量的精力(语法检查、解析、权限检查、优化、访问策略等)。重用一个数据库可以避免后续执行的工作量

与其扔掉它们,不如尝试以可重用的方式编写查询,例如忽略空输入参数:

where someCol = coalesce(?, someCol)
因此,如果将参数设置为
null
(即“未指定”),则条件成功)

或者,如果每次都必须生成查询,请在
Map
中保留对
PreparedStatements
的引用,其中生成的查询是关键,如果找到了,请重用它们。使用for you Map实现可防止内存不足

准备执行[once]-close的逻辑是否存在缺陷且基本上不受鼓励

我不认为这本身是一个问题。给定的SQL语句需要在某个时刻“准备”,无论是显式的(使用PreparedStatement)还是“动态的”(使用语句)。如果我们使用PreparedStatement而不是只执行一次的语句,可能会产生稍多的开销,但所涉及的开销不太可能很大,特别是如果您引用的语句是正确的:

典型的DBMS(Oracle、DB2、MSSQL、MySQL、Derby等)甚至不会将这样的语句提升到准备好的语句缓存中(或者至少,它们的默认JDBC驱动程序/数据源不会)

不鼓励的是这样一种模式:

for(int thing:thingList){
PreparedStatement ps=conn.prepareStatement(“{some constant SQL statement}”);
ps.setInt(1,事物);
ps.executeUpdate();
ps.close();
}
因为PreparedStatement只被使用一次,而同一个SQL语句被反复地准备(尽管如果SQL语句及其执行计划确实被缓存了,那么这可能不是什么大不了的事情。)更好的方法是

PreparedStatement ps=conn.prepareStatement(“{some constant SQL statement}”);
for(int thing:thingList){
ps.setInt(1,事物);
ps.executeUpdate();
}
ps.close();
…甚至更好,用
 .. FROM T WHERE X in (?,?,?,  … length based on the size of the collection,?,? ,?,?)