我一直在阅读博客Java Revisited,今天我正在阅读文章Why use PreparedStatement in Java JDBC - Example Tutorial。有一篇文章写过关于查询的参数化,使用API提供的参数化比连接字符串更好,更安全,更具表现性,并使用下面的例子:
SQL查询1:带字符串连接的PreparedStatement
String loanType = getLoanType();
PreparedStatement pstmt = conn.prepareStatement("select banks from loan where loan_type=" + loanType);
SQL查询2:使用PreparedStatement进行参数化查询
PreparedStatement pstmt = conn.prepareStatement("select banks from loan where loan_type=?");
pstmt.setString(1, loanType);
作者解释说,通过使用参数化,JDBC驱动程序将检查数据并且驱动程序没有连接的字符串,它将只运行发送的SQL,即,如果我们有那个着名的OR 1 = 1
查询将始终返回true,并且有经典的SQL注入案例。
我不清楚的是为什么一个人比另一个更有表现力,另外,如果我有一个总是相同的价值,这也是真的吗?
PreparedStatement pstmt = conn.prepareStatement(select * from users where active = 'S');
或者更具表现力的用法如下:
PreparedStatement pstmt = conn.prepareStatement(select * from users where active = ?);
pstmt.setString(1, "S");
另一个问题,在这种情况下我认为安全问题是一样的,因为没有参数通过用户没有攻击,对吗?
答案 0 :(得分:4)
如果参数真的永远不会改变,那么使用文字是可以接受的。但是,根据数据库平台的不同,将参数值作为将更改的文字传递可能会导致性能低于最佳。
准备语句时,数据库将解析SQL并创建执行计划以找到结果的最佳方法(使用其优化程序)。这可能涉及几个步骤,包括“硬”解析和“软”解析。
使用参数化值准备语句时,数据库将解析SQL并缓存已解析的执行计划。然后,您可以多次执行预准备语句,并仅更改发送到DB的参数值。 db可以重新使用缓存的执行计划,而不必再次“硬”解析SQL。
当您使用嵌入的文字参数重复执行SQL语句时,数据库必须对每次执行执行硬解析,因此;可能更多的处理时间。
答案 1 :(得分:1)
安全性是一样的。使用Prepared Statements增加的安全性好处是它验证用户输入以防止SQL注入。没有用户输入的地方,没有安全风险。
如果要使用不同的数据多次进行类似的数据库调用,则使用参数使用单个Prepared Statement会更有效,而不是使用字符串连接为每个实例构建新的Prepared Statement。如果它只是一次性通话,那么我无法想象性能会受到多大影响。
答案 2 :(得分:1)
如果您使用不同的参数值多次使用相同的语句,性能增益就会出现,因为准备工作 - 包括数据库引擎对SQL语句的编译和优化步骤 - 只执行一次 - 至少有一些JDBC驱动程序/数据库引擎。如果您的修复值不是来自用户,则无需使用预准备语句。从性能角度来看,也没有注射问题。
答案 3 :(得分:1)
使用PreparedStatement而不是普通Statement的主要优势(在我看来,唯一确定的优势)是参数化查询 - 这样您就可以在运行时动态接受查询(这在您需要时是一个巨大的优势最终用户的一些方向如何进行)。此外,PreparedStatement旨在优化运行查询的执行时间。简单语句将直接执行传递的查询。因此,由于您使用的是PreparedStatement,我建议您使用选项2(来自您的问题),因为使用'markers'(?符号)动态传递参数是使用PreparedStatement的优势。
现在出现的问题是,如果参数值永远不会改变,你将如何继续 - 这只是一个意见问题。在这种情况下,我的建议是使用直字符串作为查询并且不使用参数。