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

Как хранить данные EAV без схемы (Значение атрибута сущности) с помощью JSON и гибернации

Узнайте, как можно использовать типы JSON для хранения данных EAV без схемы в атрибутах сущности JPA с помощью проекта Hibernate Types с открытым исходным кодом.

Автор оригинала: Vlad Mihalcea.

Вступление

Один из моих подписчиков в Твиттере недавно спросил меня о способе хранения EAV (Сущность-Атрибут-Значение) данных с использованием JPA и гибернации, и, поскольку это очень хороший вопрос, я решил превратить его в сообщение в блоге.

Слава @vlad_mihalcea , библиотека типов гибернации вчера сэкономила мне массу работы, автоматически сопоставив столбец PostgreSQL jsonb с POJO. Блестящая работа 💪

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

Модель предметной области

Давайте предположим, что у нас есть книжный интернет-магазин, и книги моделируются с помощью объекта Книга :

@Entity(name = "Book")
@Table(name = "book")
@TypeDef(
    typeClass = JsonType.class, 
    defaultForType = JsonNode.class
)
public class Book {

    @Id
    @GeneratedValue
    private Long id;

    @NaturalId
    private String isbn;

    @Column(columnDefinition = "jsonb")
    private JsonNode properties;

    //Getters and setters omitted for brevity
}

Книга имеет уникальный атрибут isbn , поэтому она отображается как @Natural .

Для получения более подробной информации об аннотации @Natural ознакомьтесь с этой статьей .

Атрибут свойства имеет тип JsonNode , который содержит структуру данных EAV.

Поскольку Hibernate изначально не поддерживает типы JSON , нам нужен тип Json для обработки специфичного для Джексона JsonNode типа объекта.

Типы спящего режима

Первое, что вам нужно сделать, это настроить следующую зависимость Maven в вашем проекте pom.xml файл конфигурации:


    com.vladmihalcea
    hibernate-types-55
    ${hibernate-types.version}

Вот и все!

Время тестирования

При сохранении следующей Книги сущности:

Book book = new Book();
book.setIsbn( "978-9730228236" );
book.setProperties(
    JacksonUtil.toJsonNode(
        "{" +
        "   \"title\": \"High-Performance Java Persistence\"," +
        "   \"author\": \"Vlad Mihalcea\"," +
        "   \"publisher\": \"Amazon\"," +
        "   \"price\": 44.99" +
        "}"
    )
);

entityManager.persist( book );

Hibernate генерирует соответствующую инструкцию SQL INSERT:

INSERT INTO 
    book 
(
    isbn, 
    properties, 
    id
) 
VALUES 
(
    '978-9730228236', 
    '{"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99}',  
    1
)

Мы также можем извлечь сущность Book и даже изменить ее, а Hibernate позаботится обо всей логике сохранения:

Session session = entityManager.unwrap( Session.class );

Book book = session
    .bySimpleNaturalId( Book.class )
    .load( "978-9730228236" );

LOGGER.info( "Book details: {}", book.getProperties() );

book.setProperties(
    JacksonUtil.toJsonNode(
        "{" +
        "   \"title\": \"High-Performance Java Persistence\"," +
        "   \"author\": \"Vlad Mihalcea\"," +
        "   \"publisher\": \"Amazon\"," +
        "   \"price\": 44.99," +
        "   \"url\": \"https://www.amazon.com/High-Performance-Java-Persistence-Vlad-Mihalcea/dp/973022823X/\"" +
        "}"
    )
);

При запуске приведенного выше тестового случая Hibernate генерирует следующие инструкции SQL:

SELECT  b.id AS id1_0_
FROM    book b
WHERE   b.isbn = '978-9730228236'

SELECT  b.id AS id1_0_0_ ,
        b.isbn AS isbn2_0_0_ ,
        b.properties AS properti3_0_0_
FROM    book b
WHERE   b.id = 1

-- Book details: {"price":44.99,"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon"}

UPDATE 
    book 
SET 
    properties = '{"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99,"url":"https://www.amazon.com/High-Performance-Java-Persistence-Vlad-Mihalcea/dp/973022823X/"}' 
WHERE 
    id = 1

Круто, правда?

Вывод

Типы гибернации-это здорово! Даже если Hibernate поставляется с большим разнообразием типов, даже с датой и временем Java 1.8 , вы можете легко сопоставлять пользовательские типы, такие как массив или JSON.

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