`
choelea
  • 浏览: 74472 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

PreparedStatement 预编译原理

阅读更多

Databases have a tough job. They accept SQL queries from many clients concurrently and execute the queries as efficiently as possible against the data. Processing statements can be an expensive operation but databases are now written in such a way so that this overhead is minimized. However, these optimizations need assistance from the application developers if we are to capitalize on them. This article shows you how the correct use of PreparedStatements can significantly help a database perform these optimizations.

How does a database execute a statement?

Obviously, don't expect alot of detail here; we'll only examine the aspects important to this article. When a database receives a statement, the database engine first parses the statement and looks for syntax errors. Once the statement is parsed, the database needs to figure out the most efficient way to execute the statement. This can be computationally quite expensive. The database checks what indexes, if any, can help, or whether it should do a full read of all rows in a table. Databases use statistics on the data to figure out what is the best way. Once the query plan is created then it can be executed by the database engine.

It takes CPU power to do the access plan generation. Ideally, if we send the same statement to the database twice, then we'd like the database to reuse the access plan for the first statement. This uses less CPU than if it regenerated the plan a second time.

Statement Caches

Databases are tuned to do statement caches. They usually include some kind of statement cache. This cache uses the statement itself as a key and the access plan is stored in the cache with the corresponding statement. This allows the database engine to reuse the plans for statements that have been executed previously. For example, if we sent the database a statement such as "select a,b from t where c = 2", then the computed access plan is cached. If we send the same statement later, the database can reuse the previous access plan, thus saving us CPU power.

Note however, that the entire statement is the key. For example, if we later sent the statement "select a,b from t where c = 3", it would not find an access plan. This is because the "c=3" is different from the cached plan "c=2". So, for example:

For(int I = 0; I < 1000; ++I)
{
        PreparedStatement ps = conn.prepareStatement("select a,b from t where c = " + I);
        ResultSet rs = Ps.executeQuery();
        Rs.close();
        Ps.close();
}

Here the cache won't be used. Each iteration of the loop sends a different SQL statement to the database. A new access plan is computed for each iteration and we're basically throwing CPU cycles away using this approach. However, look at the next snippet:

PreparedStatement ps = conn.prepareStatement("select a,b from t where c = ?");
For(int I = 0; I < 1000; ++I)
{
        ps.setInt(1, I);
        ResultSet rs = ps.executeQuery();
        Rs.close();
}
ps.close();

Here it will be much more efficient. The statement sent to the database is parameterized using the '?' marker in the sql. This means every iteration is sending the same statement to the database with different parameters for the "c=?" part. This allows the database to reuse the access plans for the statement and makes the program execute more efficiently inside the database. This basically let's your application run faster or makes more CPU available to users of the database.

PreparedStatements and J2EE servers

Things can get more complicated when we use a J2EE server. Normally, a prepared statement is associated with a single database connection. When the connection is closed, the preparedstatement is discarded. Normally, a fat client application would get a database connection and then hold it for its lifetime. It would also create all prepared statements eagerly or lazily. Eagerly means that they are all created at once when the application starts. Lazily means that they are created as they are used. An eager approach gives a delay when the application starts but once it starts then it performs optimally. A lazy approach gives a fast start but as the application runs, the prepared statements are created when they are first used by the application. This gives an uneven performance until all statements are prepared but the application eventually settles and runs as fast as the eager application. Which is best depends on whether you need a fast start or even performance.

The problem with a J2EE application is that it can't work like this. It only keeps a connection for the duration of the request. This means that it must create the prepared statements every time the request is executed. This is not as efficient as the fat client approach where the prepared statements are created once, rather than on every request. J2EE vendors have noticed this and designed connection pooling to avoid this performance disadvantage.

When the J2EE server gives your application a connection, it isn't giving you the actual connection; you're getting a wrapper. You can verify this by looking at the name of the class for the connection you are given. It won't be a database JDBC connection, it'll be a class created by your application server. Normally, if you called close on a connection then the jdbc driver closes the connection. We want the connection to be returned to the pool when close is called by a J2EE application. We do this by making a proxy jdbc connection class that looks like a real connection. It has a reference to the actual connection. When we invoke any method on the connection then the proxy forwards the call to the real connection. But, when we call methods such as close instead of calling close on the real connection, it simply returns the connection to the connection pool and then marks the proxy connection as invalid so that if it is used again by the application we'll get an exception.

Wrapping is very useful as it also helps J2EE application server implementers to add support for prepared statements in a sensible way. When an application calls Connection.prepareStatement, it is returned a PreparedStatement object by the driver. The application then keeps the handle while it has the connection and closes it before it closes the connection when the request finishes. However, after the connection is returned to the pool and later reused by the same, or another application, , then ideally, we want the same PreparedStatement to be returned to the application.

J2EE PreparedStatement Cache

J2EE PreparedStatement Cache is implemented using a cache inside the J2EE server connection pool manager. The J2EE server keeps a list of prepared statements for each database connection in the pool. When an application calls prepareStatement on a connection, the application server checks if that statement was previously prepared. If it was, the PreparedStatement object will be in the cache and this will be returned to the application. If not, the call is passed to the jdbc driver and the query/preparedstatement object is added in that connections cache.

We need a cache per connection because that's the way jdbc drivers work. Any preparedstatements returned are specific to that connection.

If we want to take advantage of this cache, the same rules apply as before. We need to use parameterized queries so that they will match ones already prepared in the cache. Most application servers will allow you to tune the size of this prepared statement cache.

Summary

In conclusion, we should use parameterized queries with prepared statements. This reduces the load on the database by allowing it to reuse access plans that were already prepared. This cache is database-wide so if you can arrange for all your applications to use similar parameterized SQL, you will improve the efficiency of this caching scheme as an application can take advantage of prepared statements used by another application. This is an advantage of an application server because logic that accesses the database should be centralized in a data access layer (either an OR-mapper, entity beans or straight JDBC).

Finally, the correct use of prepared statements also lets you take advantage of the prepared statement cache in the application server. This improves the performance of your application as the application can reduce the number of calls to the JDBC driver by reusing a previous prepared statement call. This makes it competitive with fat clients efficiency-wise and removes the disadvantage of not being able to keep a dedicated connection.

If you use parameterized prepared statements, you improve the efficiency of the database and your application server hosted code. Both of these improvements will allow your application to improve its performance.

分享到:
评论

相关推荐

    SQL.预编译.docx

    预编译语句,即PreparedStatement,是Java.sql包中的一个接口,它是Statement接口的子接口。与普通的Statement对象不同,PreparedStatement在创建时就需要提供SQL语句,并立即发送到数据库进行编译。当执行时,...

    一文搞懂MySQL预编译

    在Java中,可以使用PreparedStatement接口实现MySQL预编译。以下是一个简单的示例: ```java Connection con = JdbcUtils.getConnection(); String sql = "select * from t_book where bid=?"; PreparedStatement ...

    练习3:使用PreparedStatement插入宠物信息.zip

    PreparedStatement的主要优势在于它的预编译能力和防止SQL注入的能力,使得代码更加安全和高效。 首先,我们需要了解PreparedStatement的工作原理。PreparedStatement对象允许程序员将SQL语句作为模板,其中包含一...

    06丨数据库原理:为什么PrepareStatement性能更好更安全?.pdf

    - **预编译机制**:PreparedStatement在执行前会先进行预编译,生成执行计划。预编译的过程可以理解为数据库对SQL语句的模板进行解析和优化,减少了每次执行时的解析开销。对于批量处理或重复执行的SQL,...

    JAVA-Web阶段重点及面试题汇总.pdf

    - PreparedStatement预编译SQL语句,适用于执行多次的SQL操作,提升效率,且有防止SQL注入的安全性。 - 防SQL注入:PreparedStatement将参数作为值处理,而非字符串拼接,避免恶意输入破坏SQL结构。 - 可读性好:...

    java操作数据库万能增,删,改

    2. **创建预编译的SQL语句**:通过`conn.prepareStatement(preparedSql)`创建PreparedStatement对象,预编译SQL语句可以提升执行效率,尤其在多次执行相同结构但不同参数的SQL时更为明显。 3. **设置预编译SQL参数*...

    在JSP中访问数据库

    接下来,我们讨论预编译语句(PreparedStatement),这是JDBC提供的一种高效且安全的SQL执行方式。预编译语句可以防止SQL注入攻击,因为它们允许在执行前对SQL语句进行验证。此外,由于数据库可以提前解析和优化预...

    sql注入原理简介

    PreparedStatement是一种预编译的SQL语句模板,它将SQL语句与数据分离,确保了SQL语句的结构不会因为用户输入而改变。Java数据库连接(JDBC)中的PreparedStatement具有防止SQL注入的功能,它不允许在不同的插入时间...

    jsp分页案例.rar

    3. **预编译与执行SQL**:使用PreparedStatement预编译SQL语句,并设置参数。预编译可以提高执行效率,防止SQL注入攻击。 4. **处理结果集**:执行SQL后,ResultSet对象会返回查询结果。遍历结果集,将其转化为Java...

    提高Java开发数据库效率的技巧.zip

    - 使用PreparedStatement预编译SQL语句,能有效防止SQL注入并提高执行效率。 - 对于重复执行的SQL,预编译可以降低解析成本。 4. **批量操作**: - 批量插入或更新数据,而不是逐条处理,可以显著减少数据库交互...

    SQL注入原理与解决方法代码示例

    - **预编译语句(PreparedStatement)**:Java的JDBC提供预编译语句接口,可以有效地防止SQL注入。预编译语句将查询模板与用户输入分开,确保用户输入的数据不会被解释为SQL代码。例如: ```java String query = ...

    jsp+servlet+mysql体育场预约系统

    数据库连接通常在Servlet中建立,通过预编译的PreparedStatement对象执行SQL命令,以防止SQL注入攻击。 4. **开发环境**: 开发者使用Eclipse作为集成开发环境(IDE),它提供了编写、调试和运行Java Web应用的...

    struts原理与实践(三)

    5. PreparedStatement:预编译的SQL语句,提供更高效、安全的SQL执行方式,可以防止SQL注入。 6. ResultSet:存储查询结果,提供了遍历和获取数据的方法。 JDBC的工作流程通常包括以下步骤: - 加载并注册数据库...

    jsp+mysql图书馆管理系统

    2. **预编译SQL语句**:对于频繁执行的操作,可以使用PreparedStatement预编译SQL语句,提高性能并防止SQL注入。 3. **设置参数**:根据操作类型(添加、编辑或删除),设置PreparedStatement中的参数值。 4. **...

    02_JDBC_day02(代码).zip

    5. **预编译语句(PreparedStatement)**:预编译的SQL语句可以提高性能,因为数据库只需解析一次,之后多次执行时只需替换参数即可。 **JDBC高级特性** 6. **批处理**:允许一次性发送多个SQL语句,提高效率,...

    SQLJ知识演示文稿

    - **SQL预编译声明**:这是SQLJ的核心,它定义了SQL语句模板,这些模板在运行时可以填充参数,类似于Java的PreparedStatement。 - **SQLJ接口**:这些接口定义了如何与数据库进行交互,包括执行SQL语句、处理结果集...

    防止SQL注入dbq

    1. **预编译语句(PreparedStatement)**:Java的JDBC提供预编译语句接口,可以有效地防止SQL注入。预编译语句将SQL语句模板与参数分开处理,如: ```java String sql = "SELECT * FROM Users WHERE ID = ?"; ...

    jdbc问题解决的方法

    此外,使用PreparedStatement预编译SQL语句可以提高执行效率。 8. **异常处理**:确保对可能出现的SQLException进行捕获并处理,避免程序因未预期的错误而崩溃。同时,记录详细的错误日志有助于问题排查。 9. **...

    javaSQL直联驱动

    3. **创建Statement/PreparedStatement**:创建Statement对象用于执行SQL语句,或者使用PreparedStatement预编译SQL,提高效率并防止SQL注入。 4. **执行SQL**:通过Statement或PreparedStatement对象执行SQL查询、...

    SQL注入原理以及Spring Boot如何防止SQL注入(含详细示例代码)

    1. 参数化查询:使用预编译的SQL语句(如PreparedStatement),将用户输入作为参数传递,而不是直接拼接到SQL字符串中。这样可以防止恶意代码改变SQL语句的结构。 2. 使用存储过程:通过存储过程与数据库交互,减少...

Global site tag (gtag.js) - Google Analytics