Перейти к основному содержимому

Преобразование набора результатов JDBC в JSON в Java

· 4 мин. чтения

1. Обзор

В некоторых сценариях нам может потребоваться отправить результат запроса к базе данных через вызов API в другую систему или платформу обмена сообщениями. Для таких случаев мы часто используем JSON в качестве формата обмена данными.

В этом руководстве мы увидим несколько способов преобразования объекта JDBC ResultSet в формат JSON .

2. Пример кода

Мы будем использовать базу данных H2 для нашего примера кода. У нас есть образец CSV-файла, который мы считываем в слова таблицы с помощью JDBC. Вот три строки из примера CSV-файла, причем первая строка является заголовком:

Username,Id,First name,Last name
doe1,7173,John,Doe
smith3,3722,Dana,Smith
john22,5490,John,Wang

Строка кода для формирования ResultSet выглядит так:

ResultSet resultSet = stmt.executeQuery("SELECT * FROM words");

Для обработки JSON мы используем библиотеку JSON-Java ( org.json ). Во- первых, мы добавляем соответствующую зависимость в наш файл POM:

<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20220320</version>
</dependency>

3. Использование внешних зависимостей

API JDBC предшествует современным платформам коллекций Java. Следовательно, мы не можем использовать подобные методы for - each и Stream .

Вместо этого мы должны полагаться на итераторы. Более того, нам нужно извлечь номер и список имен столбцов из метаданных ResultSet .

Это приводит к базовому циклу, состоящему из формирования объекта JSON для каждой строки, добавления объектов в список и, наконец, преобразования этого списка в массив JSON . Все эти функции доступны в пакете org.json :

ResultSetMetaData md = resultSet.getMetaData();
int numCols = md.getColumnCount();
List<String> colNames = IntStream.range(0, numCols)
.mapToObj(i -> {
try {
return md.getColumnName(i + 1);
} catch (SQLException e) {
e.printStackTrace();
return "?";
}
})
.collect(Collectors.toList());

JSONArray result = new JSONArray();
while (resultSet.next()) {
JSONObject row = new JSONObject();
colNames.forEach(cn -> {
try {
row.put(cn, resultSet.getObject(cn));
} catch (JSONException | SQLException e) {
e.printStackTrace();
}
});
result.add(row);
}

Здесь мы сначала запускаем цикл для извлечения имени каждого столбца. Позже мы используем эти имена столбцов при формировании результирующего объекта JSON.

Во втором цикле мы просматриваем фактические результаты и преобразовываем каждый из них в объект JSON, используя имена столбцов, вычисленные на предыдущем шаге. Затем мы добавляем все эти объекты в массив JSON.

Мы исключили из цикла извлечение имен столбцов и количества столбцов. Это помогает ускорить выполнение.

Полученный JSON выглядит так:

[
{
"Username":"doe1",
"First name":"John",
"Id":"7173",
"Last name":"Doe"
},
{
"Username":"smith3",
"First name":"Dana",
"Id":"3722",
"Last name":"Smith"
},
{
"Username":"john22",
"First name":"John",
"Id":"5490",
"Last name":"Wang"
}
]

4. Использование jOOQ с настройками по умолчанию

Фреймворк jOOQ (Java Object Oriented Querying) предоставляет, среди прочего, набор удобных служебных функций для работы с объектами JDBC и ResultSet . Во- первых, нам нужно добавить зависимость jOOQ в наш файл POM:

<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>3.11.11</version>
</dependency>

После добавления зависимости мы можем использовать однострочное решение для преобразования ResultSet в объект JSON:

JSONObject result = new JSONObject(DSL.using(dbConnection)
.fetch(resultSet)
.formatJSON());

Результирующий элемент JSON представляет собой объект, состоящий из двух полей, называемых полями и записями , где поля имеют имена и типы столбцов, а записи содержат фактические данные. Это немного отличается от предыдущего объекта JSON и выглядит следующим образом для нашей примерной таблицы:

{
"records":[
[
"doe1",
"7173",
"John",
"Doe"
],
[
"smith3",
"3722",
"Dana",
"Smith"
],
[
"john22",
"5490",
"John",
"Wang"
]
],
"fields":[
{
"schema":"PUBLIC",
"name":"Username",
"type":"VARCHAR",
"table":"WORDS"
},
{
"schema":"PUBLIC",
"name":"Id",
"type":"VARCHAR",
"table":"WORDS"
},
{
"schema":"PUBLIC",
"name":"First name",
"type":"VARCHAR",
"table":"WORDS"
},
{
"schema":"PUBLIC",
"name":"Last name",
"type":"VARCHAR",
"table":"WORDS"
}
]
}

5. Использование jOOQ с индивидуальными настройками

Если нам не нравится структура объекта JSON по умолчанию, созданная jOOQ, есть возможность ее настроить.

Мы сделаем это, реализуя интерфейс RecordMapper . Этот интерфейс имеет метод map() , который получает на вход Record и возвращает нужный объект произвольного типа.

Затем мы передаем RecordMapper в качестве входных данных для метода map() результирующего класса jOOQ:

List json = DSL.using(dbConnection)
.fetch(resultSet)
.map(new RecordMapper() {
@Override
public JSONObject map(Record r) {
JSONObject obj = new JSONObject();
colNames.forEach(cn -> obj.put(cn, r.get(cn)));
return obj;
}
});
return new JSONArray(json);

Здесь мы вернули JSONObject из метода map() .

Полученный JSON выглядит так, как в разделе 3:

[
{
"Username":"doe1",
"First name":"John",
"Id":"7173",
"Last name":"Doe"
},
{
"Username":"smith3",
"First name":"Dana",
"Id":"3722",
"Last name":"Smith"
},
{
"Username":"john22",
"First name":"John",
"Id":"5490",
"Last name":"Wang"
}
]

6. Заключение

В этой статье мы рассмотрели три различных способа преобразования JDBC ResultSet в объект JSON.

Каждый подход может иметь свое собственное применение. То, что мы выбираем, зависит, например, от требуемой структуры выходного объекта JSON и возможных ограничений на размер зависимости.

Как всегда, исходный код примеров доступен на GitHub.