Следующий код преобразует a ResultSet
в строку JSON с помощью JSONArray
и JSONObject
.
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;
import java.sql.SQLException;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
public class ResultSetConverter {
public static JSONArray convert( ResultSet rs )
throws SQLException, JSONException
{
JSONArray json = new JSONArray();
ResultSetMetaData rsmd = rs.getMetaData();
while(rs.next()) {
int numColumns = rsmd.getColumnCount();
JSONObject obj = new JSONObject();
for (int i=1; i<numColumns+1; i++) {
String column_name = rsmd.getColumnName(i);
if(rsmd.getColumnType(i)==java.sql.Types.ARRAY){
obj.put(column_name, rs.getArray(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.BIGINT){
obj.put(column_name, rs.getInt(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.BOOLEAN){
obj.put(column_name, rs.getBoolean(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.BLOB){
obj.put(column_name, rs.getBlob(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.DOUBLE){
obj.put(column_name, rs.getDouble(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.FLOAT){
obj.put(column_name, rs.getFloat(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.INTEGER){
obj.put(column_name, rs.getInt(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.NVARCHAR){
obj.put(column_name, rs.getNString(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.VARCHAR){
obj.put(column_name, rs.getString(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.TINYINT){
obj.put(column_name, rs.getInt(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.SMALLINT){
obj.put(column_name, rs.getInt(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.DATE){
obj.put(column_name, rs.getDate(column_name));
}
else if(rsmd.getColumnType(i)==java.sql.Types.TIMESTAMP){
obj.put(column_name, rs.getTimestamp(column_name));
}
else{
obj.put(column_name, rs.getObject(column_name));
}
}
json.put(obj);
}
return json;
}
}
- Есть более быстрый способ?
- Есть ли способ использовать меньше памяти?
java.sql.Types.BIGINT
имеет размер 8 байт, поэтому его нужно читать с помощьюrs.getLong()
notrs.getInt()
Ответы:
Компилятор JIT, вероятно, сделает это довольно быстро, поскольку это всего лишь ветки и базовые тесты. Возможно, вы могли бы сделать его более элегантным с помощью поиска HashMap для обратного вызова, но я сомневаюсь, что это будет быстрее. Что касается памяти, то она и так довольно тонкая.
Почему-то я сомневаюсь, что этот код на самом деле является критическим узким местом для памяти или производительности. У вас есть реальная причина попробовать его оптимизировать?
источник
Я думаю, что есть способ использовать меньше памяти (фиксированный, а не линейный объем в зависимости от количества данных), но это означает изменение сигнатуры метода. Фактически, мы можем напечатать данные Json непосредственно в потоке вывода, как только мы получим их из ResultSet: уже записанные данные будут собраны в мусор, поскольку нам не нужен массив, который хранит их в памяти.
Я использую GSON, который принимает переходники типа. Я написал адаптер типа для преобразования ResultSet в JsonArray, и он очень похож на ваш код. Я жду релиза «Gson 2.1: Target Dec 31, 2011», в котором будет «Поддержка определяемых пользователем адаптеров потокового типа». Затем я изменю свой адаптер на потоковый.
Обновить
Как и обещал, я вернулся, но не с Gson, а с Jackson 2. Извините за опоздание (на 2 года).
Предисловие: ключ к использованию меньшего объема памяти для результата находится в курсоре "на стороне сервера". С помощью этого вида курсоров (также называемых набором результатов для разработчиков Java) СУБД отправляет данные клиенту (также известному как драйвер) постепенно, когда клиент выполняет чтение. Я думаю, что курсор Oracle по умолчанию находится на стороне сервера. Для MySQL> 5.0.2 ищите useCursorFetch в параметре URL- адреса подключения . Проверьте свою любимую СУБД.
1: Итак, чтобы использовать меньше памяти, мы должны:
JSONArray
), но пишите каждую строку непосредственно в строке вывода , где для строки вывода я имею в виду выходной поток или средство записи, или также генератор json, который обертывает поток вывода или средство записи.2: Как говорится в документации Джексона:
3: Я вижу, что вы в своем коде используете getInt, getBoolean. getFloat ... из ResultSet без wasNull . Я думаю, это может вызвать проблемы.
4: Я использовал массивы для кэширования мыслей и избегания вызова геттеров на каждой итерации. Хотя я не являюсь поклонником конструкции switch / case, я использовал ее для этого
int
SQLTypes
.Ответ: еще не полностью протестирован, он основан на Jackson 2.2 :
ResultSetSerializer
Объект инструктирует Джексона о том , как сериализации (tranform объект JSON) в ResultSet. Внутри он использует API-интерфейс Jackson Streaming. Вот код теста:И, конечно же, код класса ResultSetSerializer:
источник
Две вещи, которые сделают это быстрее:
Переместите вызов
rsmd.getColumnCount()
из цикла while. Количество столбцов не должно различаться в разных строках.Для каждого типа столбца вы вызываете что-то вроде этого:
Будет немного быстрее использовать индекс столбца для получения значения столбца:
источник
String column_name;
вне цикла while.Более простое решение (на основе рассматриваемого кода):
источник
Вы можете использовать jOOQ для работы. Вам не обязательно использовать все возможности jOOQ, чтобы воспользоваться некоторыми полезными расширениями JDBC. В этом случае просто напишите:
Соответствующие используемые методы API:
DSLContext.fetch(ResultSet)
для преобразования набора результатов JDBC в результат jOOQ.Result.formatJSON()
для форматирования результата jOOQ в строку JSON.В результате форматирование будет выглядеть так:
Вы также можете довольно легко создать собственное форматирование с помощью
Result.map(RecordMapper)
По сути, это делает то же самое, что и ваш код, обходя создание объектов JSON, «передаваемых» непосредственно в файл
StringBuilder
. Я бы сказал, что накладные расходы на производительность должны быть незначительными в обоих случаях.(Отказ от ответственности: я работаю в компании, стоящей за jOOQ)
источник
"
до\"
), чтобы создать допустимую строку JSON. Это ошибкаformatJSON()
функции? Или я что-то упускаю?fetch(resultSet)
? Это нигде не определено. И если я получу JDBCResultSet
до загрузки, для чегоDSL.using(connection)
? Зачем нужно подключение? :)ResultSet
, поэтому я думаю, что в этом нет никаких сомненийResultSet
. Действительно, не совсем понятно, зачемconnection
здесь нужен. Если вы используете jOOQ, вам в любом случае будет доступенDSLContext
(результатDSL.using(connection)
или аналогичный) вариант .В дополнение к предложениям @Jim Cook. Еще одна мысль - использовать переключатель вместо if-elses:
источник
Этот ответ может быть не самым эффективным, но он наверняка динамичный. Соединив собственный JDBC с библиотекой Google Gson, я легко могу преобразовать результат SQL в поток JSON.
Я включил конвертер, пример файла свойств БД, создание таблиц SQL и файл сборки Gradle (с используемыми зависимостями).
QueryApp.java
ResultSetConverter.java
QueryHelper.java
database.properties
JDBC_Tutorial.sql
build.gradle
Полученные результаты
Базовый ВЫБОР
Промежуточный ВЫБОР
источник
Сначала предварительно сгенерируйте имена столбцов, затем используйте
rs.getString(i)
вместоrs.getString(column_name)
.Ниже приводится реализация этого:
источник
JSONObject json = resList.get(i);
Затем вы можете свободно манипулировать объектом JSONjson
.Если кто-то планирует использовать эту реализацию, вы можете проверить это и это
Это моя версия этого кода преобразования:
источник
Как и прежде, цикл if / then более эффективен, чем переключатель для перечислений. Если у вас есть переключатель против необработанного целого числа enum, тогда он более эффективен, но против переменной if / then более эффективен, по крайней мере, для Java 5, 6 и 7.
Т.е. по какой-то причине (после некоторых тестов производительности)
быстрее чем
Я вижу, что некоторые люди во мне сомневаются, поэтому я опубликую здесь код, который вы можете запустить, чтобы увидеть разницу, вместе с выводом, который у меня есть на Java 7. Результаты следующего кода с 10 значениями перечисления следующие. Обратите внимание, что ключ здесь - это if / then, использующий целочисленное значение, сравнивающее с порядковыми константами перечисления, против переключателя с порядковым значением перечисления против необработанных порядковых значений int, против переключателя с перечислением против каждого имени перечисления. If / then с целочисленным значением превзошли оба других переключателя, хотя последний переключатель был немного быстрее, чем первый переключатель, он не был быстрее, чем if / else.
If / else заняло 23 мс.
Switch заняло 45 мс.
Switch 2 заняло 30 мс.
Всего совпадений: 3000000
источник
intern()
для строк, которые больше не нужны в большинстве современных версий Java.Для всех, кто выбрал сетку if-else, используйте:
Потому что в случае псевдонимов в вашем запросе имя столбца и метка столбца - это две разные вещи. Например, если вы выполните:
Ты получишь
Скорее, чем:
источник
источник
источник
с другой стороны, здесь я использовал ArrayList и Map, поэтому он не вызывает объект json строка за строкой, а после завершения итерации набора результатов:
источник