繁体   English   中英

从 SimpleJdbcTemplate 查询返回的大列表

[英]Large list returned from a SimpleJdbcTemplate query

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

List<Event> events = 
            this.simpleJdbcTemplate.query(myQuery,
            myMapper(), 
            new Object[] {
                    filter.getFirst(),
                    filter.getSecond(),
                    filter.getThird()}
            );

问题是该列表可能包含类似 600,000 个事件...因此使用大量 memory (并且还需要时间来处理)。

但是,我真的不需要一次检索所有事件。 实际上我希望能够遍历列表,只读取几个事件(链接到特定的 KEY_ID - sql 查询 myQuery 由 KEY_ID 排序),处理它们并最终返回迭代,让垃圾收集器摆脱以前的和已经处理的事件,所以我永远不会超过一定数量的 memory。

有没有使用 Spring 库(或任何库)的好方法?

干杯,Vakimshaar。

我认为你的问题的一部分是你的查询是一次执行的,你得到的结果集是一大块,占用了 memory 和网络带宽。 除了需要一种方法来遍历结果集之外,您还需要一种方法来一次从数据库中取回结果。 看看这个关于延迟加载结果集的答案 看起来您可以结合使用 ResultSetExtractor 设置提取大小,并可能获得所需的行为(取决于数据库)。

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

Oracle 示例

SELECT * FROM ([your query]) WHERE rownum>=[start number] 
                               AND rownum<[start number + chunk size];

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

只需使用带有ResultSetExtractor作为参数的查询方法 ResultSetExtractor可以使用您的映射器将当前行转换为Event 将每个事件放入一个列表,直到您到达不同的 KEY_ID 或结果集的末尾,然后继续您的事件列表并清除列表。

也许以下代码可能对您有用?

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

这是分页查询上下文:

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

}

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

暂无
暂无

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

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