Рубрики
Без рубрики

Как сопоставить объекты JSON с помощью универсальных типов гибернации

Узнайте, как сопоставить типы столбцов JSON с атрибутами сущностей JPA, используя проект с открытым исходным кодом Hibernate Types для Oracle, SQL Server, PostgreSQL и MySQL.

Автор оригинала: 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 AtomicReference eventHolder = 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:

List participants = 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 следующим образом:

List participants = 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 обеспечивает более расширенные возможности запроса:

List participants = 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.