Jdbc流式查询与游标查询

源码基于mysql-connector-5.1

1.获取statement

PreparedStatement statement = conn.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY);
ResultSet resultSet = statement.executeQuery();

prepareStatement()方法中resultSetType的默认值为ResultSet.TYPE_FORWARD_ONLY,resultSetConcurrency的默认值为ResultSet.CONCUR_READ_ONLY
在这里插入图片描述在这里插入图片描述

2.获取结果集

com.mysql.jdbc.MysqlIO

protected ResultSetImpl getResultSet(StatementImpl callingStatement, long columnCount, int maxRows, int resultSetType, int resultSetConcurrency,
        boolean streamResults, String catalog, boolean isBinaryEncoded, Field[] metadataFromCache) throws SQLException {
    if (this.connection.versionMeetsMinimum(5, 0, 2) && this.connection.getUseCursorFetch() && isBinaryEncoded && callingStatement != null
        && callingStatement.getFetchSize() != 0 && callingStatement.getResultSetType() == ResultSet.TYPE_FORWARD_ONLY) {
        if (usingCursor) {
            RowData rows = new RowDataCursor(this, prepStmt, fields);
            ResultSetImpl rs = buildResultSetWithRows(callingStatement, catalog, fields, rows, resultSetType, resultSetConcurrency, isBinaryEncoded);
            if (usingCursor) {
                rs.setFetchSize(callingStatement.getFetchSize());
            }
            return rs;
        }
    }
    if (!streamResults) {
        rowData = readSingleRowSet(columnCount, maxRows, resultSetConcurrency, isBinaryEncoded, (metadataFromCache == null) ? fields : metadataFromCache);
    } else {
        rowData = new RowDataDynamic(this, (int) columnCount, (metadataFromCache == null) ? fields : metadataFromCache, isBinaryEncoded);
        this.streamingData = rowData;
    }

    ResultSetImpl rs = buildResultSetWithRows(callingStatement, catalog, (metadataFromCache == null) ? fields : metadataFromCache, rowData, resultSetType,
            resultSetConcurrency, isBinaryEncoded);

    return rs;
}

由以上代码得知,普通查询,流失查询与游标查询返回的结果集是不同的。

3.普通查询

默认是普通查询
在readSingleRowSet()方法中,将所有数据加到rows(ArrayList)中,返回静态行数据(RowDataStatic)
在这里插入图片描述

4.流式查询

不一次性将所有数据加载到内存,在调用next()方法时,MySQL驱动只从网络数据流获取到1条数据,然后返回应用

开启方式

  1. 设置statement.setFetchSize(Integer.MIN_VALUE);
  2. 调用createStreamingResultSet()
void enableStreamingResults() throws SQLException;
mysql-connector-8
((com.mysql.cj.jdbc.JdbcStatement)statement).enableStreamingResults()
mysql-connector-5.1
((com.mysql.jdbc.Statement)statement).enableStreamingResults()
StatementImpl.java
/**
 * We only stream result sets when they are forward-only, read-only, and the
 * fetch size has been set to Integer.MIN_VALUE
 * 
 * @return true if this result set should be streamed row at-a-time, rather
 *         than read all at once.
 */
protected boolean createStreamingResultSet() {
    return ((this.query.getResultType() == Type.FORWARD_ONLY) && (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY)
            && (this.query.getResultFetchSize() == Integer.MIN_VALUE));
}

前面两个参数是默认值,所以只需要设置fetchSize为Integer.MIN_VALUE即可,这个是mysql-connector中自定义的的特殊值,在JDBC接口中没有规范。
enableStreamingResults()也可以开启流式查询,但这不是jdbc接口。

获取数据过程

  1. 返回动态行数据
    在这里插入图片描述
  2. 每次调用resultSet.next()时,才会取一条数据
    在这里插入图片描述

5.游标查询

每次从服务器检索fetchSize行数据,调用next()遍历完后,会再次从服务器取数据。

开启方式

url=jdbc:mysql://192.168.9.19:3307/test?useCursorFetch=true
statement.setFetchSize(20);

获取数据过程

  1. 返回游标行数据
    在这里插入图片描述
  2. 每次调用resultSet.next()时,判断当前fetch的位置,如果不小于(size-1)的话,就会再次取数据
    在这里插入图片描述
  3. 在fetchMoreRows()中会给mysql发送一个带有fetchSize的fetch命令,调用nextRow取数据
    在这里插入图片描述

6. 总结

  1. 这三种方式都会调用 MysqlIO#ResultSetRow nextRow()方法获取数据。
  2. 游标查询也属于流式查询的一种,只不过每次从服务器读取的数据量可以设置。
  3. 流式查询(包含游标)会使用阻塞的方式从mysql取数据,所以必须在结果集中读取所有行(或者关闭)后才能在该连接上发起其他查询,否则会抛出异常。
    There are some caveats with this approach. You must read all of the rows in the result set (or close it) before you can issue any other queries on the connection, or an exception will be thrown.
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值