Как управлять большим набором данных с помощью 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 подсказка что касается количества строк, которые должны быть извлечены из базы данных
водитель фактически свободен применять или игнорировать подсказку. Некоторые драйверы игнорируют его, некоторые драйверы применяют его напрямую, некоторым драйверам требуется больше параметров. Драйвер JDBC MySQL попадает в последнюю категорию. Если вы проверите MySQL JDBC драйвер документация, вы увидите следующую информацию (выделите около 2/3 вниз до заголовка ResultSet):
чтобы включить эту функцию, необходимо создать экземпляр инструкции следующим образом:
stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE);
пожалуйста, читайте весь раздел документа, он также описывает предостережения этого подхода.
чтобы заставить его работать весной, вам, однако, нужно расширение / переопределение JdbcTemplate с помощью пользовательской реализации. Поскольку я не делаю весны, я не могу подробно рассказать об этом, но теперь вы, по крайней мере, знаете, где искать.
удачи.
вот весеннее решение, основанное на ответ предоставлено 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 и т. д.