Java Map to JSON mapping with 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
In this article, we are going to see how we can store Java Map entity attributes in JSON columns when using JPA, Hibernate, and the Hypersistence Utils project.
While you can also persist Java Map
entity attributes in PostgreSQL HStore columns, a JSON column type is a much more common option, especially since it works with other relational databases, like Oracle, SQL Server, or MySQL.
As you already have seen, the Hypersistence Utils project allows you to map a JSON column type to a wide variety of JPA entity attributes, like POJOs, JsonNode
, collections or String
Java object types:
- How to map POJO entity attributes as JSON using JPA and Hibernate
- How to
JsonNode
entity attributes as JSON using JPA and Hibernate - How to map a String JPA property to a JSON column using Hibernate
- How to map JSON collections using JPA and Hibernate
This article shows that you can also map JSON column types to Java Map entity attributes when using JPA and Hibernate.
Maven dependency for JSON mapping with JPA and Hibernate
The first thing we need to do is add the Hypersistence Utils dependency from the Maven Central repository. For instance, if you are using Maven, then you need to add the following dependency to your project pom.xml
configuration file:
<dependency> <groupId>io.hypersistence</groupId> <artifactId>hypersistence-utils-hibernate-60</artifactId> <version>${hypersistence-utils.version}</version> </dependency>
For older Hibernate ORM versions, you can use the hypersistence-utils-hibernate-55
or hypersistence-utils-hibernate-52
dependencies.
The Hypersistence Utils project documentation provides more details about which dependency you should use based on the Hibernate ORM version used by your project.
Domain Model
Let’s assume we have the following book
table in our relational database:
And we want to map it to a Book
entity whose properties
attribute is of the Map<String, String>
type:
Mapping the Java Map entity property to a JSON column using JPA and Hibernate
The Book
JPA entity is mapped as follows.
For Hibernate 6, the mapping will look as follows:
@Entity(name = "Book") @Table(name = "book") public class Book { @Id @GeneratedValue private Long id; @NaturalId @Column(length = 15) private String isbn; @Type(JsonType.class) @Column(columnDefinition = "jsonb") private Map<String, String> properties = new HashMap<>(); public String getIsbn() { return isbn; } public Book setIsbn(String isbn) { this.isbn = isbn; return this; } public Map<String, String> getProperties() { return properties; } public Book setProperties(Map<String, String> properties) { this.properties = properties; return this; } public Book addProperty(String key, String value) { properties.put(key, value); return this; } }
And for Hibernate 5, like this:
@Entity(name = "Book") @Table(name = "book") @TypeDef(name = "json", typeClass = JsonType.class) public class Book { @Id @GeneratedValue private Long id; @NaturalId @Column(length = 15) private String isbn; @Type(type = "json") @Column(columnDefinition = "jsonb") private Map<String, String> properties = new HashMap<>(); public String getIsbn() { return isbn; } public Book setIsbn(String isbn) { this.isbn = isbn; return this; } public Map<String, String> getProperties() { return properties; } public Book setProperties(Map<String, String> properties) { this.properties = properties; return this; } public Book addProperty(String key, String value) { properties.put(key, value); return this; } }
The @TypeDef
annotation is used to register the JsonType
, which handles JSON column types when using Oracle or PostgreSQL, SQL Server, or MySQL.
The isbn
property uses the @NaturalId
annotation, which allows us to fetch the Book
entity by its ISBN number without knowing its numerical identifier.
The properties
attribute is of the type Map<String, String>
, so it uses the @Type
annotation to reference the json
type we have registered previously via the @TypeDef
annotation.
The getters, setters, as well as the addProperty
utility method use the Fluent-style API to simplify the way we build Book
entity instances.
Testing Time
When persisting the following Book
entity:
entityManager.persist( new Book() .setIsbn("978-9730228236") .addProperty("title", "High-Performance Java Persistence") .addProperty("author", "Vlad Mihalcea") .addProperty("publisher", "Amazon") .addProperty("price", "$44.95") );
Hibernate generates the following SQL INSERT statement:
INSERT INTO book ( isbn, properties, id ) VALUES ( '978-9730228236', { "author":"Vlad Mihalcea", "price":"$44.95", "publisher":"Amazon", "title":"High-Performance Java Persistence" }, 1 )
And, when fetching the Book
entity, we can see that the properties
entity attribute is properly fetched from the database:
Book book = entityManager.unwrap(Session.class) .bySimpleNaturalId(Book.class) .load("978-9730228236"); Map<String, String> bookProperties = book.getProperties(); assertEquals( "High-Performance Java Persistence", bookProperties.get("title") ); assertEquals( "Vlad Mihalcea", bookProperties.get("author") );
Awesome, right?
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
As you can see, mapping a Java Map JPA entity property to a JSON column type is very easy when using the Hypersistence Utils project.
Hypersistence Utils offers support for many non-standard column types, not just JSON. You can use it to map ARRAY, Inet, HStore, PostgreSQL-specific Enums, nullable Character
columns, or use the enhanced ResultTransformer
instances.
If you are using JPA and Hibernate, then you should definitely use the Hypersistence Utils project as well.
