Creating Java JPA entities with a JSON field

Java JPA entities with a JSON field featured image

1. Introduction

In this tutorial we will explore how you can persist a JSON field in your JPA entity.

We use a number of technologies:

  • Spring Data JPA
  • Spring Data REST
  • Hibernate ORM
  • Jackson
  • PostgreSQL

As always, the code used in the article is available on GitHub here.

This article is focused on storing a JSON field in a JPA entity. If you would like to see how to store an XML field – check out this article here.

2. Our Goal

Our goal is to be able to perform create, read, update and delete (CRUD) operations on a JPA entity that contains a field which uses PostgreSQL’s native JSON/JSONB column type. To illustrate how to achieve this we will:

  • add the correct dependencies to our project.
  • create a simple domain model with a JSON field in an entity.
  • create a simple @RepositoryRestResource which exposes endpoints illustrating CRUD operations on the entity.

Let’s get started!

3. Adding the correct project dependencies

In order to define JSON fields within an @Entity we need to have a specific additional dependency: the hibernate types project. For maven, you can add the following to your pom.xml file:

And for gradle you can add the following to your build.gradle file:

As of Jun 2020 the latest version of the project is v.2.9.11 so that it what we will use here. You can check all of the other dependencies required for the project here.

4. Our domain model

We use an extremely simple domain model so we can focus on the addition of the JSON field:

We have a person which has an auto-generated ID, first_name, surname and address. The address is stored using PostgreSQL’s native JSONB type.

4.1 An Address (JSON type)

An address is a carrier of data (a record) which we define as such:

We use a number of convenience lombok annotations:

  • @Data will make sure, among other things, that there are getters and setter for each field.
  • @NoArgsConstructor will make sure that a constructor which takes no arguments (public Address()) exists.
  • @AllArgsConstructor will make a constructor with all of the three arguments (doorNumber, roadName, postalCode) for convenience.

We use these annotations to have the code required by the JSON serialiser (which defaults to Jackson) generated for us instead of manually writing it.

4.2 A Person (JPA entity)

We define as Person as:

  1. The @TypeDef annotation is used to create an alias for the JsonBinaryType which we can refer to as jsonb within @Type definitions in the class (which we do in (2)).
    • In this case we have placed the @TypeDef annotation on the class itself.
    • It is useful to know that it is also possible to place it on a file so that all classes within a package can refer to the @TypeDef without having to redefine it for each class.
  2. The @Type annotation is used to refer to the jsonb @TypeDef that we defined in 1.
    • This allows hibernate to know how to serialise/deserialise the field.

The rest of the class is self explanatory – it contains the ID of the Person as well as their first name and surname. The ID is generated using the IDENTITY strategy which means the database will provide the generated ID when needed.

5. CRUD operations

Let’s now illustrate how we can perform CRUD operations on the entity and have the Address persisted as JSON in the PostgreSQL database. We do this by utilising the Spring Data REST project’s functionality that allows us to easily expose a typical REST API for the Person resource. From the documentation:

Spring Data REST builds on top of Spring Data repositories, analyzes your application’s domain model and exposes hypermedia-driven HTTP resources for aggregates contained in the model.

5.1 The @RepositoryRestResource

Let’s create a @RepositoryRestResource for our Person:

Here we are stating our that:

  • the path at /people is where the endpoints should be generated.
  • when we have a related link to a single item of this resource (itemResourceRel) we should use the word person (in other words for the singular case use person).
  • when we have a related link to a collection of these resources (collectionResourceRel) we should use the word people (in other words for the plural case use people).

For our simple use case we can really just ignore the itemResourceRel and collectionResourceRel but the explanation above is there for those who want to understand why we defined them this way.

6. Testing the @RepositoryRestResource

To run the application we need to run the bootRun gradle task. With our @RepositoryRestResource being defined we can now go ahead and test the expected endpoints:

  • Creating a person resource:
    • POST /people
  • Reading a person resource:
    • GET /people/{id}
  • Updating a person resource:
    • PUT /people/{id}
  • Deleting a person resource:
    • DELETE /people/{id}

6.1 [POST] (Crud) Creating a person resource

Let’s create a new person by issuing the command.

We receive a response:

Indicating that the entity has been saved and is available at the endpoint http://localhost:8080/people/1. Let’s make a GET request to read the data at this endpoint to make sure it has been persisted.

6.2 [GET] (cRud) Reading a person resource

In the previous subsection we created (using a POST request) a Person with id=1. Let’s now try to get the data associated with the Person with id=1. Remember, the whole purpose of this article/tutorial is to illustrate how to store a JSON field using JPA. So let’s first read the data by running a SQL query to check how it is persisted in the database:

Created Entity with JSON field as is persisted in DB

We can see that the database has stored the field as JSON just as we would want. Now let’s query for the data using a HTTP request:

We receive the response as expected!

6.3 [PUT] (crUd) Updating a person resource

Let’s now update the address of the person and see how that works:

And the data has been updated in the database too:

Updated entity with JSON field as is persisted in DB

The JSON was updated as we would expect!

6.4 [DELETE] (cruD) Deleting a person resource

Let’s now delete the Person:

7. Conclusion

In this tutorial we explored how we can store a JSON field in a JPA entity backed by a PostgreSQL database which uses a table with the native jsonb type. We created a @RepositoryRestResource for a simple Person resource that stored its address as JSON. We then illustrated how we could test CRUD operations on the entity and finally we saw how instances of the entity were persisted successfully as JSON in the database.

All of the source code for the project is available here.


Leave a Reply

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