Hibernate и PostgreSQL JSON Type

    Привет хабр! В этой статье не будет глубокого анализа json типа в PostgreSQL или очередных бесполезных попыток сравнить данную возможность PostgreSQL с NoSQL базами данных типа MongoDB. Я просто расскажу про то, как использовать Hibernate и PostgreSQL json. Думаю кому-нибудь это может оказаться полезно.

    Объект внутри сущности


    Предположим у вас есть реляционная модель данных. Вполне может возникнуть ситуация, что для некоторых из сущностей необходимо хранить какой либо объект(документ, если хотите). Конечно можно расширить для этого объекта модель данных одной(а может и несколькими) сущностью, или просто хранить этот объект в виде массива байт. Но у PostgreSQL уже достаточно давно появился тип данных json способный хранить json объект в соответствии с RFC 4627. Стало интересно, как это можно использовать, и какие возможности это может дать. При первом обращении к google я нашел несколько неструктурированных постов и Q&A которые что-то объясняли, но не давали полной картины. Немного потыкавшись и разобравшись что к чему, я пришел к выводу что использование этих полей очень удобно, и решил создать маленькую библиотечку, которая упростит использование json типов. Ниже я расскажу как ей пользоваться, ну и еще немного сравнения с первыми приходящими на ум альтернативами.

    Диалект


    Конечно же ни о какой поддержке данного типа в стандартном диалекте и речь не идет. Очень хотелось бы что бы hibernate сам распознавал тип и осуществлял валидацию/обновление схемы. Поэтому библиотека содержит диалект JsonPostgreSQLDialect расширяющий PostgreSQL9Dialect. Просто используйте этот диалект в вашем persistance.xml.

    Объект для хранения


    Хранить в json поле можно как объект, так и просто Map. Если вы хотите хранить объект, то достаточно унаследовать классом который вы собираетесь хранить PGJsonObject
    public class CacheObject extends PGJsonObject {
       ...
    }
    

    и сделать с помощью аннотаций определение типа в вашей Entity:
    @Entity
    @TypeDefs({@TypeDef( name= "JsonObject", typeClass = CacheObject.class)})
    public class Product {
    ...
    @Type(type = "JsonObject")
        public CacheObject getCache() {
            return cache;
        }
    }
    

    Если это ломает вашу схему наследования, вы можете создать отдельный от вашего класса для хранения, класс отвечающий за тип. Он будет наследовать все тот же PGJsonObject, и должен переопределять метод returnedClass(), в котором будет возвращать класс объекта который вы хотите хранить. Этот класс потом нужно будет использовать в вашей Entity для определения типа.
    public class JsonCustomType extends PGJsonObject {
    
        @Override
        public Class returnedClass() {
            return Custom.class;
        }
    
    }
    

    Тогда определение типа в Entity будет выглядеть так:
    @Entity
    @TypeDefs({@TypeDef( name= "JsonObject", typeClass = JsonCustomType.class)})
    public class Product {
    ...
    @Type(type = "JsonObject")
        public Custom getCustom() {
            return custom;
        }
    }
    

    Если же вы собрались хранить Map, то просто используйте уже имеющийся в библиотеке тип JsonMapType.

    Рекомендации


    • Будьте внимательны с изменением данных объекта. Если объект, что вы храните не immutable то любые изменения что вы в нем сделаете, будут сохранены в базу(конечно если транзакция не откатиться), так что если вы не хотите сохранять изменения, делайте копию
    • Проверьте ваши нативные запросы. Убедитесь, что в них указывается ваша Entity


    Преимущества JSON поля


    Сразу на ум приходит 2 альтернативные реализации
    • Расширить схему БД для объекта
    • хранить массив байт

    Первый вариант может очень сильно усложнить вашу модель данных, в случае если объект имеет глубокую иерархию. Если объект хранит большой массив, например массив точек, то таблица с точками может иметь огромное количество записей, что может замедлить выборку. Так же при таком варианте приложение должно знать все о схеме данных, что бы вытащить их из таблицы.
    Во втором варианте не требуется расширения схемы, но информация хранящаяся в данном поле абсолютно не репрезентативная и не индексируемая. И как и в предыдущем случае приложение должно знать о структуре хранящихся данных, только на этот раз уже для правильной десериализации.
    При использовании json поля вся информация в базе представлена в удобном читаемом виде. Так же блягодаря json functions в PostgreSQL можно писать удобные native query для выборки по определенным полям json-объекта. А благодаря expression indexes вы можете повесить индекс на любое поле вашего json-объекта.
    Так же использование json полей может послужить большим подспорьем в задачах по интеграции. Сохраняя в одном месте в json поле объект, в другом вы можете вытаскивать из него Map, и наоборот. Если вы знаете только часть структуры объекта, вы можете десериализовать только известную и нужную вам часть json-объекта в java-объект, а остальную либо игнорировать, либо десериализовать опять же в Map.

    Исходный код проекта можно найти на github. Пока что проект сырой. Надеюсь в будущем упростить использование, ввести аннотации.
    Поделиться публикацией

    Похожие публикации

    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое