执行查询后,PostgreSQL JDBC 驱动程序版本9.2-1002 何时从服务器获取行?它是在查询执行之后(在客户端应用程序调用PreparedStatement.executeQuery()
之后)立即获取行,还是在客户端应用程序第一次调用ResultSet.next()
以从结果集中检索一行之后?这是否取决于语句获取大小的值?
3 回答
如以下程序所示,PreparedStatement.executeQuery()
始终从服务器检索结果集中的行。该程序还演示了语句提取大小如何影响行检索。在语句的默认提取大小为零的情况下,executeQuery()
从服务器检索所有行并ResultSet.next()
从内存而不是服务器检索并返回下一行。(程序甚至可以在执行查询后关闭连接,并且next()
仍然可以遍历所有行。)在 fetch size 非零的情况下,executeQuery()
检索第一批行,其数量等于 fetch size,并且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);
}}}}}