Обработка больших записей в приложении Java EE
Это стол phonenumbers
с двумя столбцами: id
, а также number
, Есть около half a million entries
в таблице. База данных MySQL
,
Требуется разработать простое приложение Java EE, подключенное к этой базе данных, которое позволяет пользователю загружать все number
значения в comma separated style
следуя определенному URL.
Если мы получим все значения в огромном String array
а затем объединить их (с запятой между всеми значениями) в String
а затем отправить его пользователю, это звучит как правильное решение?
Приложение не является общедоступным и будет использоваться ограниченным нет. людей.
3 ответа
Лучше всего не хранить данные в памяти Java каким-либо образом, а просто записывать полученные данные в ответ сразу же по мере поступления данных. Также необходимо настроить драйвер MySQL JDBC для обслуживания набора результатов строка за строкой. от Statement#setFetchSize()
согласно документации драйвера JDBC MySQL, в противном случае он будет кешировать все это в памяти.
Предполагая, что вы знакомы с сервлетами, вот базовый пример, который учитывает все это:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
response.setHeader("Content-Disposition", "attachment;filename=numbers.txt"); // Force download popup.
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
Writer writer = response.getWriter();
try {
connection = database.getConnection();
statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
statement.setFetchSize(Integer.MIN_VALUE);
resultSet = statement.executeQuery("SELECT number FROM phonenumbers");
while (resultSet.next()) {
writer.write(resultSet.getString("number"));
if (!resultSet.isLast()) {
writer.write(",");
}
}
} catch (SQLException e) {
throw new ServletException("Query failed!", e);
} finally {
if (resultSet != null) try { resultSet.close; } catch (SQLException logOrIgnore) {}
if (statement != null) try { statement.close; } catch (SQLException logOrIgnore) {}
if (connection != null) try { connection.close; } catch (SQLException logOrIgnore) {}
}
}
Есть еще кое-что для правильного форматирования вывода CSV. Было бы проще использовать существующую библиотеку, такую как эта, для создания выходного файла.
Вы можете сгенерировать вывод в файл на диске (на веб-сервере), а затем перенаправить браузер на этот файл (с помощью задания cron или чего-либо еще, чтобы очистить старые данные) или просто передать результат непосредственно обратно пользователю.
Если вы осуществляете потоковую передачу напрямую, убедитесь, что для MIME-типа задано значение, которое будет запускать загрузку в браузере пользователя (например, text/csv или text/comma-separated-values)
Если бы использовать Mysql 5.1+, я бы просто использовал проприетарный синтаксис, чтобы вывести куда-нибудь файл и передать его в ответе сервлета.
SELECT a,b,a+b INTO OUTFILE '/tmp/result.txt'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM test_table;
http://dev.mysql.com/doc/refman/5.1/en/select.html
Для стольких записей, если вы все еще хотите использовать JDBC, вы можете попробовать следующее:
- получить количество записей, выбрать несколько записей (используя ограничение запроса) и записать их
- если вы достигнете количества записей в чанке, вы получите другую, пока не достигнете максимального количества записей