簡體   English   中英

PostgreSQL JDBC 驅動程序在執行查詢后何時獲取行?

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

PostgreSQL JDBC 驅動程序版本9.2-1002 何時在執行查詢后從服務器獲取行? 它是在查詢執行后立即獲取行(在客戶端應用程序調用PreparedStatement.executeQuery() )還是在客戶端應用程序首先調用ResultSet.next()以從結果集中檢索行之后? 這是否取決於語句提取大小的值?

如以下程序所示, PreparedStatement.executeQuery()始終從服務器檢索結果集中的行。 該程序還演示了語句提取大小如何影響行檢索。 在語句的默認獲取大小為零的情況下, executeQuery()從服務器檢索所有行,而ResultSet.next()從內存而不是從服務器檢索並返回下一行。 (程序甚至可以在執行查詢后關閉連接, next()仍然可以遍歷所有行。)在 fetch size 非零的情況下, executeQuery()檢索第一批行,其數量等於獲取大小, ResultSet.next()再次從內存中返回下一行,直到它消耗了當前批次中的所有行,此時它從服務器檢索下一批行。 這種模式一直重復,直到ResultSet.next()從服務器檢索到一個空批次(一個包含零行的批次)。

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;

爪哇

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();
        }
    }
}

看看這個文檔

默認情況下,驅動程序一次收集查詢的所有結果。 這對於大型數據集可能很不方便,因此 JDBC 驅動程序提供了一種方法,可以將 ResultSet 建立在數據庫游標的基礎上,並且只獲取少量行。

少量行緩存在連接的客戶端,當耗盡時,通過重新定位游標來檢索下一個行塊。

進一步閱讀這個

通常,libpq 收集 SQL 命令的整個結果並將其作為單個 PGresult 返回給應用程序。 這對於返回大量行的命令是不可行的。 對於這種情況,應用程序可以在單行模式下使用 PQsendQuery 和 PQgetResult。 在這種模式下,結果行一次一個返回給應用程序,因為它們是從服務器接收的。

PG 要求連接為 AutoCommit = false 以獲取行作為游標。 因此,如果您使用 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