Introduction
This article uses the old 1.2.0 version of the hibernate-types project.
The API has greatly changed ever since, so you should read this article instead.
The open-source hibernate-types project allows you to map Java objects or Jackson JsonNode as JPA entity properties.
Recently, thanks to our awesome contributors, we added support for type-safe collections to be persisted as JSON as well. In this article, you are going to see how to achieve this goal.
Maven dependency
First of all, you need to set up the following Maven dependency in your project pom.xml configuration file:
<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>1.2.0</version>
</dependency>
If you’re using older versions of Hibernate, check out the hibernate-types GitHub repository for more info about the matching dependency for your current Hibernate version.
Domain Model
Let’s assume we have the following Location Java object type.
public class Location implements Serializable {
    private String country;
    private String city;
    //Getters and setters omitted for brevity
    @Override
    public String toString() {
        return "Location{" +
                "country='" + country + '\'' +
                ", city='" + city + '\'' +
                '}';
    }
}
And, one Event entity:
@Entity(name = "Event")
@Table(name = "event")
public class Event extends BaseEntity {
    @Type(type = "jsonb")
    @Column(columnDefinition = "jsonb")
    private Location location;
    @Type(
        type = "jsonb",
        parameters = {
            @org.hibernate.annotations.Parameter(
                name = TypeReferenceFactory.FACTORY_CLASS,
                value = "com.vladmihalcea.hibernate.type.json.PostgreSQLGenericJsonBinaryTypeTest$AlternativeLocationsTypeReference"
            )
        }
    )
    @Column(columnDefinition = "jsonb")
    private List<Location> alternativeLocations = new ArrayList<Location>();
    //Getters and setters omitted for brevity
}
The BaseEntity defines some basic properties (e.g. @Id, @Version) and several customs Hibernate types, among which, we are interested in the JsonBinaryType one.
@TypeDefs({
    @TypeDef(name = "string-array", typeClass = StringArrayType.class),
    @TypeDef(name = "int-array", typeClass = IntArrayType.class),
    @TypeDef(name = "json", typeClass = JsonStringType.class),
    @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class),
    @TypeDef(name = "jsonb-node", typeClass = JsonNodeBinaryType.class),
    @TypeDef(name = "json-node", typeClass = JsonNodeStringType.class),
})
@MappedSuperclass
public class BaseEntity {
    @Id
    private Long id;
    @Version
    private Integer version;
    //Getters and setters omitted for brevity
}
For more details about using @MappedSuperclass, check out this article.
TypeReferenceFactory
To store the Location object in a jsonb PostgreSQL column, we just need to annotate the location property with @Type(type = "jsonb").
However, for the alternativeLocations collection, we need to provide the associated Jackson TypeReference so that we can reconstruct the very same type-safe Java collection when reading the JSON object from the relational database.
For this purpose, we provide the fully-qualified class of the TypeReferenceFactory implementation which looks as follows:
public static class AlternativeLocationsTypeReference 
    implements TypeReferenceFactory {
    
    @Override
    public TypeReference<?> newTypeReference() {
        return new TypeReference<List<Location>>() {};
    }
}
That’s it!
Testing time
When saving the following Event entity:
Location cluj = new Location();
cluj.setCountry("Romania");
cluj.setCity("Cluj-Napoca");
Location newYork = new Location();
newYork.setCountry("US");
newYork.setCity("New-York");
Location london = new Location();
london.setCountry("UK");
london.setCity("London");
Event event = new Event();
event.setId(1L);
event.setLocation(cluj);
event.setAlternativeLocations(
    Arrays.asList(newYork, london)
);
entityManager.persist(event);
Hibernate will generate the following SQL INSERT statement:
INSERT INTO event (
    version, 
    alternativeLocations, 
    location, 
    id
) 
VALUES (
    0, 
    [
        {"country":"US","city":"New-York"},
        {"country":"UK","city":"London"}
    ], 
    {"country":"Romania","city":"Cluj-Napoca"}, 
    1
)
Also, when retrieving back the Event entity, both the location and thealternativeLocations` properties are properly fetched:
Event event = entityManager.find(Event.class, eventId);
assertEquals(
    "Cluj-Napoca", 
    event.getLocation().getCity()
);
assertEquals(2, event.getAlternativeLocations().size());
assertEquals(
    "New-York", 
    event.getAlternativeLocations().get(0).getCity()
);
assertEquals(
    "London", 
    event.getAlternativeLocations().get(1).getCity()
);
Cool, right?
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
The hibernate-types project supports more than JSON types. You can map PostgreSQL ARRAY types or PostgreSQL-specific Enums, nullable Character, or even provide your own immutable Hibernate custom Types.
Author: Vlad Mihalcea
I work as a Hibernate Developer Advocate, I wrote the High-Performance Java Persistence book and video course series. I speak at conferences and I created several open-source projects, like FlexyPool, hibernate-types, and db-util.
 
							

 steinhauer.software
steinhauer.software
João Reis Jr May 4, 2018
Thank you very much, I’m looking for a solution to persist a coordinates { latitude, longitude } array field and I think that I’ve found it. wd
João Reis Jr May 4, 2018
cant find TypeReferenceFactory 🙁
Vlad Mihalcea May 4, 2018 — Post Author
That’s because the API has changed in 2.0, and this is using the 1.x API.
Check out this article for an updated version which is even simpler than the old alternative with
TypeReferenceFactoryVictor July 24, 2018
Thank you ! This is very useful topic
vladmihalcea September 3, 2018
If you liked this article, I bet you are going to love my High-Performance Java Persistence book as well.