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

Сохранение объекта JSON с помощью Hibernate

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

1. Обзор

Для некоторых проектов может потребоваться сохранение объектов JSON в реляционной базе данных.

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

Доступно несколько фреймворков, обеспечивающих эту функциональность, но мы рассмотрим несколько простых, универсальных вариантов, использующих только Hibernate и Jackson .

2. Зависимости

В этом руководстве мы будем использовать базовую зависимость Hibernate Core :

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.0.Final</version>
</dependency>

Мы также будем использовать Jackson в качестве библиотеки JSON:

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>

Обратите внимание, что эти методы не ограничиваются этими двумя библиотеками. Мы можем заменить наш любимый поставщик JPA и библиотеку JSON.

3. Сериализация и десериализация методов

Самый простой способ сохранить объект JSON в реляционной базе данных — преобразовать объект в строку перед его сохранением. Затем мы преобразуем его обратно в объект при извлечении из базы данных.

Мы можем сделать это несколькими способами.

Первый, который мы рассмотрим, — это использование пользовательских методов сериализации и десериализации.

Мы начнем с простой сущности Customer , которая хранит имя и фамилию клиента, а также некоторые атрибуты этого клиента.

Стандартный объект JSON будет представлять эти атрибуты как HashMap , поэтому мы будем использовать его здесь:

@Entity
@Table(name = "Customers")
public class Customer {

@Id
private int id;

private String firstName;

private String lastName;

private String customerAttributeJSON;

@Convert(converter = HashMapConverter.class)
private Map<String, Object> customerAttributes;
}

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

Во- первых, мы создадим метод сериализации, который будет принимать наши атрибуты клиента и преобразовывать их в строку JSON :

public void serializeCustomerAttributes() throws JsonProcessingException {
this.customerAttributeJSON = objectMapper.writeValueAsString(customerAttributes);
}

Мы можем вызвать этот метод вручную перед сохранением или вызвать его из метода setCustomerAttributes , чтобы при каждом обновлении атрибутов обновлялась и строка JSON.

Далее мы создадим метод для десериализации строки JSON обратно в объект HashMap при извлечении Customer из базы данных:

public void deserializeCustomerAttributes() throws IOException {
this.customerAttributes = objectMapper.readValue(customerAttributeJSON, HashMap.class);
}

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

Итак, сохранение и извлечение нашего объекта Customer будет выглядеть примерно так:

@Test
public void whenStoringAJsonColumn_thenDeserializedVersionMatches() {
Customer customer = new Customer();
customer.setFirstName("first name");
customer.setLastName("last name");

Map<String, Object> attributes = new HashMap<>();
attributes.put("address", "123 Main Street");
attributes.put("zipcode", 12345);

customer.setCustomerAttributes(attributes);
customer.serializeCustomerAttributes();

String serialized = customer.getCustomerAttributeJSON();

customer.setCustomerAttributeJSON(serialized);
customer.deserializeCustomerAttributes();

assertEquals(attributes, customer.getCustomerAttributes());
}

4. Преобразователь атрибутов

Если мы используем JPA 2.1 или выше , мы можем использовать AttributeConverters для оптимизации этого процесса.

Сначала мы создадим реализацию AttributeConverter . Мы будем повторно использовать наш код из предыдущего:

public class HashMapConverter implements AttributeConverter<Map<String, Object>, String> {

@Override
public String convertToDatabaseColumn(Map<String, Object> customerInfo) {

String customerInfoJson = null;
try {
customerInfoJson = objectMapper.writeValueAsString(customerInfo);
} catch (final JsonProcessingException e) {
logger.error("JSON writing error", e);
}

return customerInfoJson;
}

@Override
public Map<String, Object> convertToEntityAttribute(String customerInfoJSON) {

Map<String, Object> customerInfo = null;
try {
customerInfo = objectMapper.readValue(customerInfoJSON, Map.class);
} catch (final IOException e) {
logger.error("JSON reading error", e);
}

return customerInfo;
}

}

Затем мы говорим Hibernate использовать наш новый AttributeConverter для поля customerAttributes , и мы закончили:

@Convert(converter = HashMapConverter.class)
private Map<String, Object> customerAttributes;

При таком подходе нам больше не нужно вручную вызывать методы сериализации и десериализации, поскольку Hibernate позаботится об этом за нас. Мы можем просто сохранять и извлекать объект Customer в обычном режиме.

5. Вывод

В этой статье мы видели несколько примеров того, как сохранять объекты JSON с помощью Hibernate и Jackson.

В нашем первом примере рассматривался простой совместимый метод с использованием пользовательских методов сериализации и десериализации. И во-вторых, мы представили AttributeConverters как мощный способ упростить наш код.

Как всегда, обязательно ознакомьтесь с исходным кодом этого руководства на Github .