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 hibernate-types
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.
Kudos to @vlad_mihalcea, the Hibernate Types library saved me tons of work yesterday by automagically mapping the PostgreSQL jsonb column to a POJO. Brilliant work 💪
— Tomasz Knyziak (@TomaszKnyziak) September 18, 2019
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-55</artifactId> <version>${hibernate-types.version}</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 = "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 }
The BaseEntity
defines some basic properties (e.g. @Id
, @Version
) and several custom Hibernate types, among which, we are interested in the JsonType
one.
@TypeDefs({ @TypeDef(name = "string-array", typeClass = StringArrayType.class), @TypeDef(name = "int-array", typeClass = IntArrayType.class), @TypeDef(name = "json", typeClass = JsonType.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.
To store both the Location
object or the List<Location>
in a jsonb
PostgreSQL column, we just need to annotate the location
property with @Type(type = "json")
.
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 the
alternativeLocations` 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
.
