How to map a JSON collection using JPA and Hibernate

Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?

Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.

So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!

Introduction

The open-source Hypersistence Utils project allows you to map Java objects or Jackson JsonNode as JPA or Hibernate entity properties, and, thanks to our awesome contributors, we have added support for storing type-safe JSON collections.

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>io.hypersistence</groupId>
    <artifactId>hypersistence-utils-hibernate-55</artifactId>
    <version>${hypersistence-utils.version}</version>
</dependency>

If you’re using older versions of Hibernate, check out the Hypersistence Utils 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 that is mapped as follows.

For Hibernate 6, the mapping will look as follows:

@Entity(name = "Event")
@Table(name = "event")
public class Event extends BaseEntity {

    @Type(JsonType.class)
    @Column(columnDefinition = "jsonb")
    private Location location;

    @Type(JsonType.class)
    @Column(columnDefinition = "jsonb")
    private List<Location> alternativeLocations = new ArrayList<Location>();

    //Getters and setters omitted for brevity
}

And for Hibernate 5, like this:

@Entity(name = "Event")
@Table(name = "event")
@TypeDef(name = "json", typeClass = JsonType.class)
public class Event extends BaseEntity {

    @Type(type = "json")
    @Column(columnDefinition = "jsonb")
    private Location location;

    @Type(type = "json")
    @Column(columnDefinition = "jsonb")
    private List<Location> alternativeLocations = new ArrayList<Location>();

    //Getters and setters omitted for brevity
}

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?

I'm running an online workshop on the 11th of October about High-Performance SQL.

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

Conclusion

The Hypersistence Utils project supports more than JSON types. You can map PostgreSQL ARRAY types or PostgreSQL-specific Enums, nullable Character, or even provide your own custom immutable Hibernate Types.

Transactions and Concurrency Control eBook

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.