Предисловие
В производственной среде подкачка базы данных часто используется для управления объемом данных, получаемых за один раз, и часто при обработке данных используется другой сценарий:
Прочитайте все данные из одной таблицы базы данных для обработки и сохраните результат в другой базе данных или файле или базе данных NoSql.
В настоящее время для пакетной обработки можно также использовать метод подкачки, но этот метод не только логически сложен, но и очень неэффективен.Есть ли способ чтения базы данных, аналогичный файловому потоку? Ответ — да.Стандарт, предложенный JDBC, не только включает потоковое чтение, но также поддерживает считывание и обновление данных в процессе чтения, но в этой статье обсуждается только потоковое (курсорное) чтение базы данных. Давайте посмотрим на реализацию курсора нативного JDBC:
Собственная реализация JDBC
@Test
public void testCursor() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager
.getConnection("jdbc:mysql://127.0.0.1:3306/db?useUnicode=true&characterEncoding=utf8&useSSL=false",
"root", "root");
PreparedStatement preparedStatement =
connection.prepareStatement("select * from table",
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
preparedStatement.setFetchSize(Integer.MIN_VALUE);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
System.err.println(resultSet.getString("id"));
}
connection.close();
}
Примечание в MySql
conn.prepareStatement("select * from table",
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
// 这里因为MySQL驱动实现使用Integer.MIN_VALUE来判断是否使用流的方式
preparedStatement.setFetchSize(Integer.MIN_VALUE);
Эти две линии незаменимы.
В PostgreSql требуется
connection.setAutoCommit(false);
// 流每一次读取的数据量
preparedStatement.setFetchSize(1000);
tips:
Видно, что разные поставщики баз данных по-прежнему имеют разные реализации.Эта статья только тестируетPostgreSqlЧто касается MySQL, то читатели могут самостоятельно протестировать другие базы данных.
После выполнения видно, что это уже потоковое чтение, поэтому перейдем к делу.Большинство проектов используют JdbcTemplate.Поддерживает ли JdbcTemplate потоковое чтение?
Использование шаблона Jdbc
Откройте JdbcTemplate, и вы увидите сигнатуру метода запроса, как показано ниже:
/**
* Query using a prepared statement, reading the ResultSet on a per-row basis
* with a RowCallbackHandler.
* <p>A PreparedStatementCreator can either be implemented directly or
* configured through a PreparedStatementCreatorFactory.
* @param psc a callback that creates a PreparedStatement given a Connection
* @param rch a callback that will extract results, one row at a time
* @throws DataAccessException if there is any problem
* @see PreparedStatementCreatorFactory
*/
void query(PreparedStatementCreator psc, RowCallbackHandler rch) throws DataAccessException;
Ключевой момент заключается в параметрах PreparedStatementCreator и RowCallbackHandler. Мы можем управлять созданием PreparedStatement и извлечением ResultSet, что очень просто. Конкретный код реализован следующим образом:
MySQL
jdbcTemplate.query(con -> {
PreparedStatement preparedStatement =
con.prepareStatement("select * from table",
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
preparedStatement.setFetchSize(Integer.MIN_VALUE);
preparedStatement.setFetchDirection(ResultSet.FETCH_FORWARD);
return preparedStatement;
}, rs -> {
while (rs.next()) {
System.err.println(resultSet.getString("id"));
}
});
PostgreSql
jdbcTemplate.query(con -> {
con.setAutoCommit(false);
PreparedStatement preparedStatement =
con.prepareStatement("select * from table",
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
preparedStatement.setFetchSize(1000);
preparedStatement.setFetchDirection(ResultSet.FETCH_FORWARD);
return preparedStatement;
}, rs -> {
while (rs.next()) {
System.err.println(resultSet.getString("id"));
}
});
Таким образом, нам больше не нужно писать уродливый код шаблона JDBC.
Ссылаться на:docs.Oracle.com/Java Color/Picture О...