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

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

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

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

现在我的问题是:

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


当前回答

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

其他回答

我遵循了这个问题的所有答案,使用- 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对象,然后最终不能利用批处理,因为我的场景要求-有数千个UPDATE或INSERT查询,所有这些查询碰巧是不同的。

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

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

PreparedStatement是一种非常好的防御(但不是万无一失的),可以防止SQL注入攻击。绑定参数值是防止“小Bobby表”进行不必要访问的好方法。

引用自mattjames

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

没什么可补充的,

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

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

PreparedStatement的优点:

Precompilation and DB-side caching of the SQL statement leads to overall faster execution and the ability to reuse the same SQL statement in batches. Automatic prevention of SQL injection attacks by builtin escaping of quotes and other special characters. Note that this requires that you use any of the PreparedStatement setXxx() methods to set the values 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(); and thus don't inline the values in the SQL string by string-concatenating. 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]); } } Which can be used as below: 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();