有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

PreparedStatement如何在Java中内部工作:SQL的编译和缓存如何工作

我在互联网上读到过这方面的消息,但我无法回答一些问题

Q1据说在执行PreparedStatement时,DBMS只需运行PreparedStatement SQL语句,而不必首先编译它。我的问题是什么时候准备发言。调用execute()会发生什么?(我的理解是:不带任何参数的sql被发送到DBMS,由DBMS编译和缓存以供将来使用。然后发送参数,DBMS用占位符和执行替换它们。)

Q2如果下次我执行PreparedSteement。使用相同的sql执行(),然后会发生什么?(我的理解是:DBMS将sql与以前的sql进行比较,如果匹配,则采用已编译的sql、替换参数和执行。)

Q3如果我偶尔调用数据库,那么prepared语句将无助于提高性能,因为在此期间数据库缓存将被清除。因此每次都会编译sql。对吧?


共 (3) 个答案

  1. # 1 楼答案

    java.sql.PreparedStatement是JRE定义的API

    execute()的实际内部行为取决于DBMS供应商提供的JDBC驱动程序实现

    例如,有一些嵌入式数据库(SQLite,H2),其中sending statement to server毫无意义

    您应该查阅您正在使用的JDBC驱动程序的文档

  2. # 2 楼答案

    除了rkosegi上面的回答之外,我还想进一步描述一下如何使用PostgreSQL驱动程序,因为它突出了这里的一些困难

    使用PostgreSQL驱动程序(请参见documentation),行为实际上是可配置的,并且相当复杂

    PostgreSQL查询协议允许单独发送查询和参数,因此PreparedStatement API不需要执行请求数据库准备语句然后稍后执行的过程。折衷的办法是,在初始准备过程中,服务器端准备会带来额外的开销,以及何时重新使用查询计划的问题,但这与较短的查询重新使用启动时间相抵消。因此,如果您正在进行优化,并且重复查找要准备的单个记录,而您正在可变范围上运行或不重复使用语句,那么您可能不会这样做。这是在数据库连接上配置的。这通常设置为一个阈值,用于确定在一定量的重复使用之后何时开始缓存计划

    当不使用服务器端prepare时,驱动程序将在同一命令中分别向数据库发送参数化查询和参数。然后,服务器解析查询,将变量插入适当的位置,并规划。计划运行后将被丢弃

    然后使用服务器端prepare,但是它可以按照您所描述的那样工作

    所以这里有一个很难的例子,说明为什么你不能问这个问题并得到一个明确的答案。JDBC提供了一个编程接口,驱动程序提供了详细的工作原理。prepareStatement未定义是否缓存查询计划。那是由司机决定的

  3. # 3 楼答案

    preparedStatement需要DBMS的支持

    如果sql已经编译,DB将缓存它。当同一个参数再次出现时,只需发送参数即可完成缓存的sql

    prepareStatement有三个优点:

    1. 使代码更清晰,以便更容易阅读

    2. 尽可能提高性能。重新计算编译时间

    3. 最重要的是,它使sql更加安全。如果您的sql如下所示:

      String sql = "select * from users where userid = " + userid; // use statement
      

      有人给它一个userid值,比如

      userid = "1;delete users;";
      

      该语句将按如下方式执行sql

      "select * from users where userid=1;"
      "delete users;"
      

    如果操作员真的有权这样做,那么对数据库来说这是一个非常危险的操作

    如果我们使用preparestatement

    String sql = "select * from users where userid = ?"; // use preparestatement
    

    数据库将把sql编译为"select * from users where userid = ''“并等待参数”?“这意味着sql将像这样执行

    "select * from users where userid = '1;delete users;'  ;" // of course, it will select 0 column. 
    

    将参数视为字符串。 这是接口java.sql.Connection类中的注释。读一下

    /**
     * Creates a <code>PreparedStatement</code> object for sending
     * parameterized SQL statements to the database.
     * <P>
     * A SQL statement with or without IN parameters can be
     * pre-compiled and stored in a <code>PreparedStatement</code> object. This
     * object can then be used to efficiently execute this statement
     * multiple times.
     *
     * <P><B>Note:</B> This method is optimized for handling
     * parametric SQL statements that benefit from precompilation. If
     * the driver supports precompilation,
     * the method <code>prepareStatement</code> will send
     * the statement to the database for precompilation. Some drivers
     * may not support precompilation. In this case, the statement may
     * not be sent to the database until the <code>PreparedStatement</code>
     * object is executed.  This has no direct effect on users; however, it does
     * affect which methods throw certain <code>SQLException</code> objects.
     * <P>
     * Result sets created using the returned <code>PreparedStatement</code>
     * object will by default be type <code>TYPE_FORWARD_ONLY</code>
     * and have a concurrency level of <code>CONCUR_READ_ONLY</code>.
     * The holdability of the created result sets can be determined by
     * calling {@link #getHoldability}.
     *
     * @param sql an SQL statement that may contain one or more '?' IN
     * parameter placeholders
     * @return a new default <code>PreparedStatement</code> object containing the
     * pre-compiled SQL statement
     * @exception SQLException if a database access error occurs
     * or this method is called on a closed connection
     */
    PreparedStatement prepareStatement(String sql)
        throws SQLException;