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 .