简体   繁体   English

PostgreSQL JDBC 驱动程序在执行查询后何时获取行?

[英]When does the PostgreSQL JDBC driver fetch rows after executing a query?

When does the PostgreSQL JDBC driver version 9.2-1002 fetch rows from the server after executing a query? PostgreSQL JDBC 驱动程序版本9.2-1002 何时在执行查询后从服务器获取行? Does it fetch the rows immediately after query execution (after the client application invokes PreparedStatement.executeQuery() ) or after the client application first invokes ResultSet.next() to retrieve a row from the result set?它是在查询执行后立即获取行(在客户端应用程序调用PreparedStatement.executeQuery() )还是在客户端应用程序首先调用ResultSet.next()以从结果集中检索行之后? Does this depend on the value of the statement fetch size?这是否取决于语句提取大小的值?

As the following program demonstrates, PreparedStatement.executeQuery() always retrieves rows in the result set from the server.如以下程序所示, PreparedStatement.executeQuery()始终从服务器检索结果集中的行。 The program also demonstrates how statement fetch size impacts row retrieval.该程序还演示了语句提取大小如何影响行检索。 In the case where the statement has the default fetch size of zero, executeQuery() retrieves all rows from the server and ResultSet.next() retrieves and returns the next row from memory, not from the server.在语句的默认获取大小为零的情况下, executeQuery()从服务器检索所有行,而ResultSet.next()从内存而不是从服务器检索并返回下一行。 (The program may even close the connection after executing the query and next() can still iterate over all rows.) In the case where fetch size is non-zero, executeQuery() retrieves the first batch of rows, the number of which equals the fetch size, and ResultSet.next() again returns the next row from memory until it consumes all rows in the current batch, at which point it retrieves the next batch of rows from the server. (程序甚至可以在执行查询后关闭连接, next()仍然可以遍历所有行。)在 fetch size 非零的情况下, executeQuery()检索第一批行,其数量等于获取大小, ResultSet.next()再次从内存中返回下一行,直到它消耗了当前批次中的所有行,此时它从服务器检索下一批行。 This pattern repeats until ResultSet.next() retrieves an empty batch from the server (one that contains zero rows).这种模式一直重复,直到ResultSet.next()从服务器检索到一个空批次(一个包含零行的批次)。

SQL SQL

-- Create table "test" and insert 2,000,000 integers from 1 up to 2,000,000.
WITH RECURSIVE t(n) AS
(
  VALUES (1)
  UNION ALL
  SELECT n+1
  FROM t
  WHERE n < 2000000
)
SELECT n as value
INTO test
FROM t;

Java爪哇

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import java.util.Properties;

public class Start
{
    public static void main( String[] args ) throws InterruptedException, SQLException
    {
        try
        {
            Class.forName( "org.postgresql.Driver" );
        }
        catch ( ClassNotFoundException e )
        {
            System.out.println( "Where is your JDBC Driver?" );
            e.printStackTrace();
            return;
        }

        System.out.println( "Registered JDBC driver" );
        Connection connection = null;

        try
        {
            final String databaseUrl = "jdbc:postgresql://localhost:5483/postgres";
            final Properties properties = new Properties();
            connection = DriverManager.getConnection( databaseUrl, properties );
            connection.setAutoCommit(false);
            Statement statement = connection.createStatement();

            // Default fetch size of 0 does not create a cursor.
            // Method executeQuery will retrieve all rows in result set.
            statement.setFetchSize( 0 );

            // Fetch size of 1 creates a cursor with batch size of 1.
            // Method executeQuery will retrieve only 1 row in the result set.
            //statement.setFetchSize( 1 );

            System.out.println( new Date() + ": Before execute query" );
            ResultSet result =
                statement.executeQuery( "select * from test" );
            System.out.println( new Date() + ": After execute query" );
            System.out.println( new Date() + ": Sleep for 5 s" );
            Thread.sleep( 5000 );
            System.out.println( new Date() + ": Before process result set" );
            while ( result.next() );
            System.out.println( new Date() + ": After process result set" );
            result.close();
            statement.close();
        }
        catch ( SQLException e )
        {
            System.out.println( "Connection failed!" );
            e.printStackTrace();
            return;
        }
        finally
        {
            if ( connection != null )
                connection.close();
        }
    }
}

Look at this documentation看看这个文档

By default the driver collects all the results for the query at once.默认情况下,驱动程序一次收集查询的所有结果。 This can be inconvenient for large data sets so the JDBC driver provides a means of basing a ResultSet on a database cursor and only fetching a small number of rows.这对于大型数据集可能很不方便,因此 JDBC 驱动程序提供了一种方法,可以将 ResultSet 建立在数据库游标的基础上,并且只获取少量行。

A small number of rows are cached on the client side of the connection and when exhausted the next block of rows is retrieved by repositioning the cursor.少量行缓存在连接的客户端,当耗尽时,通过重新定位游标来检索下一个行块。

Further read this进一步阅读这个

Ordinarily, libpq collects a SQL command's entire result and returns it to the application as a single PGresult.通常,libpq 收集 SQL 命令的整个结果并将其作为单个 PGresult 返回给应用程序。 This can be unworkable for commands that return a large number of rows.这对于返回大量行的命令是不可行的。 For such cases, applications can use PQsendQuery and PQgetResult in single-row mode.对于这种情况,应用程序可以在单行模式下使用 PQsendQuery 和 PQgetResult。 In this mode, the result row(s) are returned to the application one at a time, as they are received from the server.在这种模式下,结果行一次一个返回给应用程序,因为它们是从服务器接收的。

PG requires the connection to be AutoCommit = false for getting rows as cursor. PG 要求连接为 AutoCommit = false 以获取行作为游标。 So if you use Spring jdbc and TransactionManagement you can use connection from Transaction, which has AutoCommit = false by default.因此,如果您使用 Spring jdbc 和 TransactionManagement,您可以使用来自 Transaction 的连接,默认情况下它具有 AutoCommit = false。

@Repository()
public class SampleRepoImpl implements SampleRepo {

    private static final String SQL = "select s.id from generate_series(1,100000) as s(id)";

    @Autowired
    private DataSource dataSource;

    @Override
    @Transactional(readOnly = true)
    public void findAsCursor(RecordProcessor recordProcessor) throws SQLException {

        // It shouldn't close by hands, it will when the transaction finished.
        Connection connection = DataSourceUtils.getConnection(dataSource);

        try (PreparedStatement ps = connection.prepareStatement(SQL, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)) {
            ps.setFetchSize(100);
            try (ResultSet rs = ps.executeQuery();) {
                while (rs.next()) {
                    long id = rs.getLong("id");
                    System.out.println("id = " + id);
    }}}}}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM