更新 2 否决票是应得的,并鼓励我编写一个更强大的测试:使用 java 1.8、ojdbc8.jar 针对非生产 Oracle 12C 数据库,我通常是该数据库的唯一用户, 我运行了 100 次选择的 10 次迭代:50 次使用PreparedStatement,50 次使用Statement(使用字符串连接构建查询)。 为了尝试排除某种数据库缓存,我再次运行测试,但交换了顺序,首先运行语句,然后运行PreparedStatements。 无论顺序如何,性能都没有显着差异。请参阅下面的结果(以毫秒为单位,包含平均值和标准差)。
更新:感谢您的所有意见。我更新了标题。我在研究这个问题时发现了很多陈词滥调(“PreparedStatements 总是更快”,“PreparedStatements 总是更慢”),但没有什么明确的,我最终编写了一个测试,发现在这种情况下,PreparedStatements 始终约为 25%慢一点。
最初的问题: 我有一个带有简单查询的静态方法,用于检查数据库中是否已存在 ID:
public static boolean isPersisted(Integer id) {
String sql = "select id from table where id = "+id+"";
我知道它们可以防止 SQL 注入,并且我知道它们在循环插入带有 ID 列表的插入时提供了很大的好处(例如),但是,在这里使用准备好的语句是否有任何性能/内存优势?
id
是为应用程序本身干净地生成的(例如作为简单的计算结果),则准备好的语句没有任何好处。如果必须使用各种
id
值多次执行查询,则使用准备好的查询将获得很高的性能增益,因为 SQL 引擎只会解析该查询一次。如果
id
参数来自用户界面,则您必须使用准备好的语句,除非您打算容易受到SQL注入的攻击。长话短说,它曾经是针对编码不良的 Web 应用程序的常见攻击,并允许攻击者执行任意 SQL 命令。
我想你正在寻找这样的东西:
public static boolean isPersisted(Integer id) {
String sql = "select id from table where id = ?";
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, id);
PreparedStatement
已预编译。这意味着它们已被验证、转义和缓存(有关此问题中
PreparedStatement
预编译的更多详细信息当我说准备好的语句已预编译时是什么意思?) 预编译意味着所有工作只需完成一次,从而避免每次执行查询时执行此操作所需的所有成本。
但一如既往,如果使用得当,魔法就会实现。您可以使用
PreparedStatement
连接创建一个
String
,但要注意最终字符串中的任何更改都将导致新的预编译(从而失去好处)。SQL 注入也是如此。始终使用占位符
?
。如果你在
PreparedStatement
中连接字符串(没有什么可以阻止你做这样的事情),你很容易陷入 SQL 注入,例如:
connection.prepareStatement("Select * from MY_TABLE where email = '" + userInputEmail + "'")
切勿执行上述操作。是一个 PreparedStatement
,但可以进行 SQL 注入。因此,要从
PreparedStatements
中受益,请正确使用它们。