How to map a String JPA property to a JSON column using Hibernate
Introduction
In this article, I want to show you how you can map a String JPA property to a JSON database column using the hibernate-types
open-source project.
Although it’s probably more common to use a JsonNode
or POJO (Plain Old Java Object) on the Java side, the hibernate-types
framework is very flexible and allows you to use a String
JPA property type to represent a JSON structure.
How to map a String JPA property to a JSON column using #Hibernate @vlad_mihalcea https://t.co/6ttwyQks7v pic.twitter.com/fuXWYAXrCy
— Java (@java) February 7, 2019
Domain Model
Considering we have a book
database table that defines a properties
column of the jsonb
PostgreSQL type.
The associated Book
JPA entity can be mapped as follows:
@Entity(name = "Book") @Table(name = "book") @TypeDef( name = "jsonb", typeClass = JsonBinaryType.class ) public static class Book { @Id @GeneratedValue private Long id; @NaturalId private String isbn; @Type(type = "jsonb") @Column(columnDefinition = "jsonb") private String properties; //Getters and setters omitted for brevity }
The isbn
property is mapped using the Hibernate-specific @NaturalId
annotation which allows you to retrieve the entity by its natural identifier.
The properties
JPA attribute encodes various book-related properties in a JSON String
object. From the JPA @Column
definition, we can see that the associated database column is of the type jsonb
.
Now, since Hibernate does not provide a native Type
to handle JSON database columns, we need to need to use the JsonBinaryType
offered by the hibernate-types
library.
To use the hibernate-types
library in your project, just add the following Maven dependency:
<dependency> <groupId>com.vladmihalcea</groupId> <artifactId>hibernate-types-52</artifactId> <version>${hibernate-types.version}</version> </dependency>
If you’re using an older version of Hibernate, go to the
hibernate-types
GitHub repository and find the matchinghibernate-types
dependency for your current Hibernate version.
Testing time
When persisting the following Book
entity:
entityManager.persist( new Book() .setIsbn("978-9730228236") .setProperties( "{" + " \"title\": \"High-Performance Java Persistence\"," + " \"author\": \"Vlad Mihalcea\"," + " \"publisher\": \"Amazon\"," + " \"price\": 44.99" + "}" ) );
Hibernate generates the following SQL INSERT statement:
INSERT INTO book ( isbn, properties, id ) VALUES ( '978-9730228236', '{"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99}', 1 )
The JsonBinaryType
binds the JSON String
using the setObject
method of the PreparedStatement
object.
Notice the Fluent-style API used when creating the
Book
entity. For more details about building entities using a Fluent-style API, check out this article.
Now, when fetching the previously persisted Book
entity:
Book book = entityManager .unwrap(Session.class) .bySimpleNaturalId(Book.class) .load("978-9730228236"); assertTrue(book.getProperties().contains("\"price\": 44.99"));
We can see that the properties
attribute is properly populated by the JsonBinaryType
.
Cool, right?
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
Although creating a custom Hibernate Type
is straightforward, it’s much more convenient to use the hibernate-types
open-source project since you only need to add one dependency and specify which custom Type
you want to use via the @TypeDef
annotation.
Download free ebook sample

If you subscribe to my newsletter, you'll get:
- A free sample of my Video Course about running Integration tests at warp-speed using Docker and tmpfs
- 3 chapters from my book, High-Performance Java Persistence,
- a 10% discount coupon for my book.
Pingback: Java Weekly, Issue 267 | Baeldung
Great article.
I think it should also address the difficulty for searching (like queries based on the json columns), and how this will be RDBMS specific.
The only option is to use a native SQL query, and you can achieve database portability like this.
We achieved this by registering custom functions @json_extract@ and @json_extract_text@ and implemented custom dialects for PostgreSQL, MySQL and Oracle.
You can find the code here https://github.com/axelor/axelor-development-kit/tree/master/axelor-core/src/main/java/com/axelor/db/hibernate
I think hibernate should add support for json fields (hibernate specific feature).
Registering the database-specific functions can be done. But there are JSON operators that won’t work. Eventually, yes, this should be supported by Hibernate. Meanwhile, you can use the hibernate-types library.
Yes, json operators are database specific so can’t be supported. That’s why we choose to create custom functions json_extract and json_extract_text which are actually not native functions (in some db they are) but maps to the equivalent native functions.