Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 从SimpleJdbcTemplate查询返回的大型列表_Java_Sql_Spring_Jdbc_Spring Jdbc - Fatal编程技术网

Java 从SimpleJdbcTemplate查询返回的大型列表

Java 从SimpleJdbcTemplate查询返回的大型列表,java,sql,spring,jdbc,spring-jdbc,Java,Sql,Spring,Jdbc,Spring Jdbc,这是我的问题:在Java程序中的某个时候,我使用Spring的SimpleJdbcTemplate类从数据库中获得了一个(非常)大的事件列表 List<Event> events = this.simpleJdbcTemplate.query(myQuery, myMapper(), new Object[] { filter.getFirst(),

这是我的问题:在Java程序中的某个时候,我使用Spring的SimpleJdbcTemplate类从数据库中获得了一个(非常)大的事件列表

List<Event> events = 
            this.simpleJdbcTemplate.query(myQuery,
            myMapper(), 
            new Object[] {
                    filter.getFirst(),
                    filter.getSecond(),
                    filter.getThird()}
            );
列出事件=
this.simpleJdbcTemplate.query(myQuery,
myMapper(),
新对象[]{
filter.getFirst(),
filter.getSecond(),
filter.getThird()}
);
问题是列表可能包含600000个事件。。。因此使用了大量内存(并且需要花费时间进行处理)

但是,我并不需要一次检索所有事件。实际上,我希望能够遍历列表,只读取几个事件(链接到特定的KEY\u ID-sql查询myQuery按KEY\u ID排序),处理它们并最终返回迭代,让垃圾收集器除去以前和已经处理的事件,这样我就不会超过一定的内存量

使用Spring库(或任何库)有没有一种很好的方法可以做到这一点

干杯,
Vakimshaar。

您应该构造SQL查询以返回以特定数字开头的有限项集。这是特定于数据库的操作(在Oracle和MySql中,您将以某种形式操纵
rownum
)。然后,增加起始号码重复呼叫,直到处理完所有元素

Oracle示例

SELECT * FROM ([your query]) WHERE rownum>=[start number] 
                               AND rownum<[start number + chunk size];
从([您的查询])中选择*其中rownum>=[起始编号]

和rownum如果我理解正确,您希望迭代结果集,但对构建完整的结果列表不感兴趣


就用这个作为论据吧。
ResultSetExtractor
可以使用映射器将当前行转换为
事件。将每个事件放入一个列表中,直到到达不同的密钥ID或结果集的末尾,然后继续处理事件列表并清除该列表。

我认为部分问题在于,您的查询是一次执行的,而结果集是一大块,占用了内存和网络带宽。除了需要一种迭代结果集的方法外,还需要一种一次从数据库返回一点结果的方法。
看一看。看起来您可以结合使用ResultSetTextRactor设置提取大小,并可能获得所需的行为(取决于数据库)。

以下代码可能对您有用吗

protected <T> List<T> queryPagingList(final String query, final PagingQueryContext context, final ParameterizedRowMapper<T> mapper, final SqlParameter... parameters) throws DataAccessException {
    final Integer count = context.getCount();
    final Integer beginIndex = context.getBeginIndex();
    final List<SqlParameter> parameterList = Arrays.asList(parameters);
    final PreparedStatementCreatorFactory preparedStatementCreatorFactory = new PreparedStatementCreatorFactory(query, parameterList);
    preparedStatementCreatorFactory.setResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE);
    preparedStatementCreatorFactory.setNativeJdbcExtractor(new NativeJdbcExtractorAdapter() {
        @Override
        public PreparedStatement getNativePreparedStatement(final PreparedStatement ps) throws SQLException {
            ps.setFetchSize(count + 1);
            ps.setMaxRows((beginIndex * count) + 1);
            return ps;
        }

        @Override
        public Statement getNativeStatement(final Statement stmt) throws SQLException {
            stmt.setFetchSize(count + 1);
            stmt.setMaxRows((beginIndex * count) + 1);
            return stmt;
        }
    });
    final PreparedStatementCreator psc = preparedStatementCreatorFactory.newPreparedStatementCreator(parameterList);
    final ResultSetExtractor<List<T>> rse = new ResultSetExtractor<List<T>>() {
        public List<T> extractData(final ResultSet rs) throws SQLException {
            if (count > 0) {
                rs.setFetchSize(count + 1);
                if (beginIndex > 0) {
                    rs.absolute((beginIndex - 1) * count);
                }
            }
            rs.setFetchDirection(ResultSet.FETCH_FORWARD);
            final List<T> results = new ArrayList<T>(count + 1);
            for (int rowNumber = 0; rs.next(); ++rowNumber) {
                if (count > 0 && rowNumber > count) {
                    break;
                }
                results.add(mapper.mapRow(rs, rowNumber));
                rs.last();
                context.setTotalResults(rs.getRow());
           }
            return results;
        }
    };
    return this.simpleJbcTemplate.query(psc, null, rse);
}

它在获取大小中添加了一个,以便您可以查看是否会有更多结果。另外,根据您正在使用的JDBC驱动程序如何实现
rs.last()
,您可能不希望在
ResultSetExtractor
中使用该调用,而放弃使用
totalRows
。当调用
last()
时,某些驱动程序可能会加载所有数据。

此解决方案不意味着对数据库的调用与对密钥ID的调用一样多吗?其目的也是只调用数据库一次:调用本身需要30秒,因此我希望迭代生成的结果,而不在本地检索所有数据(如果可能的话):)它将是
行数/块大小
查询。单个查询可能比单个查询更快。不过,也许有更好的解决方案。试一试,看看会发生什么。+1代表“看看会发生什么”。我的猜测是,使用单查询方法,由于底层查询不是惰性的,因此无论如何提取结果集,都会导致内存膨胀和可能的网络饱和。但是试试看。@Nathan我的观察结果是,使用大型结果集进行查询的大量时间都花在传输数据上。事实上,数据库很大,我知道对特定密钥ID(带有WHERE子句)的请求与对所有密钥ID的请求所用的时间相同。。。另外,我事先不知道块大小…顺便说一下:使用SimpleJdbcTemplate,您不必像所有相关方法一样将参数包装在对象数组中。(当然,编译器在内部也做同样的事情,但您的代码可读性更高)感谢您的回答,我可能会尝试一下,返回一个迭代器:)
public class PagingQueryContext implements Serializable {
    private static final long serialVersionUID = -1887527330745224117L;

    private Integer beginIndex = 0;
    private Integer count = -1;
    private Integer totalResults = -1;

    public PagingQueryContext() {
    }

    public Integer getBeginIndex() {
        return beginIndex;
    }

    public void setBeginIndex(final Integer beginIndex) {
        this.beginIndex = beginIndex;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(final Integer count) {
        this.count = count;
    }

    public Integer getTotalResults() {
        return totalResults;
    }

    public void setTotalResults(final Integer totalResults) {
        this.totalResults = totalResults;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((beginIndex == null) ? 0 : beginIndex.hashCode());
        result = prime * result + ((count == null) ? 0 : count.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof PagingQueryContext)) {
            return false;
        }
        final PagingQueryContext other = (PagingQueryContext) obj;
        if (beginIndex == null) {
            if (other.beginIndex != null) {
                return false;
            }
        } else if (!beginIndex.equals(other.beginIndex)) {
            return false;
        }
        if (count == null) {
            if (other.count != null) {
                return false;
            }
        } else if (!count.equals(other.count)) {
            return false;
        }
        return true;
    }

}