Как управлять большим набором данных, используя Spring MySQL и RowCallbackHandler

Я пытаюсь просмотреть каждую строку таблицы в MySQL, используя Spring и JdbcTemplate. Если я не ошибаюсь, это должно быть так просто:

JdbcTemplate template = new JdbcTemplate(datasource);
template.setFetchSize(1);
// template.setFetchSize(Integer.MIN_VALUE) does not work either            
template.query("SELECT * FROM cdr", new RowCallbackHandler() {
  public void processRow(ResultSet rs) throws SQLException {
    System.out.println(rs.getString("src"));
  }
});

Я получаю OutOfMemoryError, потому что он пытается прочитать все это. Есть идеи?

3 ответа

Решение

Statement#setFetchSize() Javadoc уже заявляет:

Предоставляет драйверу JDBC подсказку о количестве строк, которые следует извлечь из базы данных.

Водитель может свободно применять или игнорировать подсказку. Некоторые драйверы игнорируют его, некоторые драйверы применяют его напрямую, некоторым драйверам нужно больше параметров. Драйвер MySQL JDBC попадает в последнюю категорию. Если вы посмотрите документацию драйвера JDBC для MySQL, вы увидите следующую информацию (прокрутите примерно на 2/3 до заголовка ResultSet):

Чтобы включить эту функцию, вам нужно создать экземпляр Statement следующим образом:

stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);

Пожалуйста, прочитайте весь раздел документа, в нем также описаны недостатки этого подхода.

Чтобы заставить его работать в Spring, вам нужно будет расширить / переопределить JdbcTemplate с помощью пользовательской реализации. Поскольку я не делаю Spring, я не могу подробно рассказать об этом, но теперь вы, по крайней мере, знаете, где искать.

Удачи.

Вот решение Spring, основанное на ответе, предоставленном BalusC.

class StreamingStatementCreator implements PreparedStatementCreator {
    private final String sql;

    public StreamingStatementCreator(String sql) {
        this.sql = sql;
    }

    @Override
    public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
        final PreparedStatement statement = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
        statement.setFetchSize(Integer.MIN_VALUE);
        return statement;
    }
}

Где-то в вашем коде:

DataSource dataSource = ...;
RowCallbackHandler rowHandler = ...;
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.query(new StreamingStatementCreator("SELECT * FROM huge_table"), rowHandler);

Если вы подозреваете, что получаете ошибку OutOfMemory из-за чтения всей таблицы, почему бы вам не попробовать разделить ваш запрос. Используйте фильтры, предложение LIMIT и т. Д.

Другие вопросы по тегам