Hibernate and PostgreSQL JSON Type

Published on August 28, 2014

Hibernate and PostgreSQL JSON Type

    Hi Habr! In this article, there will be no in-depth analysis of json type in PostgreSQL or any further futile attempts to compare this feature of PostgreSQL with NoSQL databases like MongoDB. I'll just talk about how to use Hibernate and PostgreSQL json. I think someone might find it useful.

    An object inside an entity


    Suppose you have a relational data model. It may well arise that for some of the entities it is necessary to store some kind of object (a document, if you want). Of course, you can expand the data model for this object with one (or maybe several) entity, or simply store this object as an array of bytes. But PostgreSQL has long had a json data type capable of storing a json object in accordance with RFC 4627 . It became interesting how it can be used, and what opportunities it can give. When you first googleI found several unstructured posts and Q&A that explained something, but did not give a complete picture. Having a little poked around and figured out what's what, I came to the conclusion that the use of these fields is very convenient, and decided to create a small library that will simplify the use of json types. Below I will tell you how to use it, well, and a little more comparison with the first alternatives that come to mind.

    Dialect


    Of course, there is no question of any support for this type in the standard dialect. I would really like for hibernate to recognize the type itself and validate / update the scheme. Therefore, the library contains a JsonPostgreSQLDialect dialect extending PostgreSQL9Dialect. Just use this dialect in your persistance.xml.

    Object to store


    You can store in json field both an object and just Map. If you want to store an object, then just inherit the class that you are going to store PGJsonObject
    public class CacheObject extends PGJsonObject {
       ...
    }
    

    and make annotations type definition in your Entity:
    @Entity
    @TypeDefs({@TypeDef( name= "JsonObject", typeClass = CacheObject.class)})
    public class Product {
    ...
    @Type(type = "JsonObject")
        public CacheObject getCache() {
            return cache;
        }
    }
    

    If this breaks your inheritance scheme, you can create a class that is separate from your class for the type. It will inherit the same PGJsonObject, and must override the returnedClass () method, in which it will return the class of the object you want to store. This class will then need to be used in your Entity to determine the type.
    public class JsonCustomType extends PGJsonObject {
        @Override
        public Class returnedClass() {
            return Custom.class;
        }
    }
    

    Then the type definition in Entity will look like this:
    @Entity
    @TypeDefs({@TypeDef( name= "JsonObject", typeClass = JsonCustomType.class)})
    public class Product {
    ...
    @Type(type = "JsonObject")
        public Custom getCustom() {
            return custom;
        }
    }
    

    If you are going to store a Map, then just use the JsonMapType type already in the library.

    Recommendations


    • Be careful with changing object data. If the object that you are storing is not immutable, then any changes that you make in it will be saved to the database (of course, if the transaction is not rolled back), so if you do not want to save the changes, make a copy
    • Check your native queries. Make sure your Entity is listed.


    Benefits of JSON Fields


    2 alternative implementations immediately come to mind
    • Extend the database schema for an object
    • store byte array

    The first option can greatly complicate your data model if the object has a deep hierarchy. If the object stores a large array, for example, an array of points, then the table with points can have a huge number of records, which can slow down the selection. Also, with this option, the application should know everything about the data scheme in order to pull them out of the table.
    In the second option, the scheme does not need to be expanded, but the information stored in this field is absolutely not representative and not indexable. And as in the previous case, the application should know about the structure of the stored data, only this time for proper deserialization.
    When using the json field, all the information in the database is presented in a convenient readable form. Also thanks to json functionsIn PostgreSQL, you can write convenient native queries to select specific fields of a json object. And thanks to expression indexes, you can hang an index on any field of your json object.
    Also, using json fields can be of great help in integration tasks. By storing an object in one place in the json field, in another you can pull a Map from it, and vice versa. If you know only part of the structure of the object, you can deserialize only the known and necessary part of the json object into a java object, and either ignore the rest or deserialize again in Map.

    The source code for the project can be found on github . So far, the project is crude. I hope to simplify the use in the future, introduce annotations.