Автор оригинала: Vlad Mihalcea.
Вступление
В этой статье мы рассмотрим, как можно сопоставить столбец JSON с атрибутом сущности JPA, используя Типы гибернации проект с открытым исходным кодом.
Хотя вы можете создавать свои собственные типы гибернации, для сопоставления типов столбцов JSON в Oracle, SQL Server, PostgreSQL или MySQL вам не нужно реализовывать свой собственный тип гибернации, поскольку проект Типы гибернации уже предлагает эту функциональность.
Слава @vlad_mihalcea , библиотека типов гибернации вчера сэкономила мне массу работы, автоматически сопоставив столбец PostgreSQL jsonb с POJO. Блестящая работа 💪
Модель Предметной области
Предполагая, что у нас есть следующая модель предметной области:
Местоположение
и Билет
являются JSON Объектом(объектами)
, в то время как Событие
и Участник
являются объектами JPA. Наша цель-предоставить JSON для гибернации Тип
, который работает для любого типа Java объекта JSON
и для любой реляционной базы данных, поддерживающей столбцы JSON.
Зависимость от Maven
Первое, что вам нужно сделать, это настроить следующую зависимость Maven в вашем проекте pom.xml
файл конфигурации:
com.vladmihalcea hibernate-types-55 ${hibernate-types.version}
Объявление типов гибернации
Чтобы использовать типы гибернации JSON, мы должны объявить их с помощью аннотации @TypeDef
:
@TypeDefs({ @TypeDef(name = "json", typeClass = JsonType.class) }) @MappedSuperclass public class BaseEntity { //Code omitted for brevity }
Аннотации “TypeDef” могут быть применены к базовому классу сущностей или в package-info.java файл, связанный с пакетом вашей текущей сущности.
MySQL
MySQL 5.7 добавляет поддержку типов JSON, которые на уровне JDBC должны быть материализованы как Строка
. Однако тип Json
умный знает, как обращаться с каждой базой данных, поэтому беспокоиться не о чем.
Сопоставление сущностей выглядит следующим образом:
@Entity(name = "Event") @Table(name = "event") public class Event extends BaseEntity { @Type(type = "json") @Column(columnDefinition = "json") private Location location; public Location getLocation() { return location; } public void setLocation(Location location) { this.location = location; } } @Entity(name = "Participant") @Table(name = "participant") public class Participant extends BaseEntity { @Type(type = "json") @Column(columnDefinition = "json") private Ticket ticket; @ManyToOne private Event event; public Ticket getTicket() { return ticket; } public void setTicket(Ticket ticket) { this.ticket = ticket; } public Event getEvent() { return event; } public void setEvent(Event event) { this.event = event; } }
При вставке следующих объектов:
final AtomicReferenceeventHolder = new AtomicReference<>(); final AtomicReference participantHolder = new AtomicReference<>(); doInJPA(entityManager -> { Event nullEvent = new Event(); nullEvent.setId(0L); entityManager.persist(nullEvent); Location location = new Location(); location.setCountry("Romania"); location.setCity("Cluj-Napoca"); Event event = new Event(); event.setId(1L); event.setLocation(location); entityManager.persist(event); Ticket ticket = new Ticket(); ticket.setPrice(12.34d); ticket.setRegistrationCode("ABC123"); Participant participant = new Participant(); participant.setId(1L); participant.setTicket(ticket); participant.setEvent(event); entityManager.persist(participant); eventHolder.set(event); participantHolder.set(participant); });
Hibernate генерирует следующие инструкции:
INSERT INTO event (location, id) VALUES (NULL(OTHER), 0) INSERT INTO event (location, id) VALUES ('{"country":"Romania","city":"Cluj-Napoca"}', 1) INSERT INTO participant (event_id, ticket, id) VALUES (1, {"registrationCode":"ABC123","price":12.34}, 1)
Объекты JSON должным образом материализуются в соответствующие столбцы базы данных.
Мало того, что JSON Объекты
должным образом преобразованы из их представления базы данных:
Event event = entityManager.find(Event.class, eventHolder.get().getId()); assertEquals("Cluj-Napoca", event.getLocation().getCity()); Participant participant = entityManager.find( Participant.class, participantHolder.get().getId()); assertEquals("ABC123", participant.getTicket().getRegistrationCode());
Но мы даже можем выдавать собственные SQL-запросы на основе JSON:
Listparticipants = entityManager.createNativeQuery( "SELECT p.ticket -> \"$.registrationCode\" " + "FROM participant p " + "WHERE JSON_EXTRACT(p.ticket, \"$.price\") > 1 ") .getResultList();
И JSON Объект(ы)
может быть изменен:
event.getLocation().setCity("Constanța"); entityManager.flush();
Спящий режим, генерирующий соответствующую инструкцию ОБНОВЛЕНИЯ:
UPDATE event SET location = '{"country":"Romania","city":"Constanța"}' WHERE id = 1
PostgreSQL
PostgreSQL поддерживает типы JSON с версии 9.2. Существует два типа , которые можно использовать:
json
jsonb
Оба типа JSON PostgreSQL должны быть материализованы с использованием двоичного формата данных, но универсальный тип Json
может справиться с этим просто отлично.
Тип столбца PostgreSQL JSON
Для типа JSON
столбца два сопоставления JSON объектов
должны быть изменены следующим образом:
@Type(type = "json") @Column(columnDefinition = "json") private Location location; @Type(type = "json") @Column(columnDefinition = "json") private Ticket ticket;
Вставка работает точно так же, как и обновление сущности, и мы даже можем запросить столбец JSON
следующим образом:
Listparticipants = entityManager.createNativeQuery( "SELECT p.ticket ->>'registrationCode' " + "FROM participant p " + "WHERE p.ticket ->> 'price' > '10'") .getResultList();
Тип столбца PostgreSQL JSONB
Для типа столбца JSONB
нам нужно только изменить атрибут ColumnDefinition
, поскольку типы столбцов json
и jsonb
PostgreSQL обрабатываются JsonType
:
@Type(type = "json") @Column(columnDefinition = "jsonb") private Location location; @Type(type = "json") @Column(columnDefinition = "jsonb") private Ticket ticket;
Вставка и JSON Объект
обновление работают одинаково, в то время как тип столбца JSONB обеспечивает более расширенные возможности запроса:
Listparticipants = entityManager.createNativeQuery( "SELECT jsonb_pretty(p.ticket) " + "FROM participant p " + "WHERE p.ticket ->> 'price' > '10'") .getResultList();
Вывод
Поддержка типов гибернации для сопоставления столбцов JSON очень полезна, и вы можете использовать ее для сопоставления атрибутов сущностей, которые являются POJO, String
или даже JsonNode
.
Самое лучшее в проекте Hibernate Types заключается в том, что он предлагает поддержку типов столбцов Oracle, SQL Server, PostgreSQL или MySQL JSON.