How to Store JSON Data Using JPA and Hibernate

This tutorial shows how to store, query, and manipulate JSON data in PostgreSQL using JPA and Hibernate, covering native JSON/JSONB types, the Hypersistence JsonType, entity mapping, service and REST layers, WildFly configuration, and curl‑based testing.

JakartaEE China Community
JakartaEE China Community
JakartaEE China Community
How to Store JSON Data Using JPA and Hibernate

Many relational databases now provide native support for JSON, including storage, indexing, querying, and manipulation. PostgreSQL, MySQL, SQL Server, and Oracle all offer JSON data types, with PostgreSQL supporting both json and jsonb since version 9.2.

PostgreSQL JSON/JSONB Example

Using PostgreSQL, create a temporary table with a jsonb column, insert a JSON document, and select it back:

create temp table test (data jsonb);
INSERT INTO test (data) VALUES ('{"name": "John", "age": 30, "email": "[email protected]"}');
SELECT * FROM test;

The result shows the stored JSON object. The article recommends preferring jsonb for better storage and index efficiency, especially on PostgreSQL 9.4+.

Mapping JSON Types in Hibernate/JPA

Hibernate and JPA lack a built‑in JSON type, but a custom type can be bound using org.hibernate.annotations.Type. The Hypersistence library provides io.hypersistence.utils.hibernate.type.json.JsonType for this purpose.

import io.hypersistence.utils.hibernate.type.json.JsonType;
import org.hibernate.annotations.Type;

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

Alternatively, a Map<String, String> can be mapped:

@Type(JsonType.class)
@Column(columnDefinition = "jsonb")
private Map<String, String> properties = new HashMap<>();

Entity Class

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Type(JsonType.class)
    @Column(columnDefinition = "jsonb")
    private Map<String, String> properties = new HashMap<>();

    private String name;
    private String surname;
    // getters/setters omitted
}

Service and REST Controller

The service class provides methods to insert an employee and to query employees by a JSON key using a PostgreSQL native query:

@ApplicationScoped
public class EmployeeService {
    @PersistenceContext
    private EntityManager em;

    @Transactional
    public void insertEmployee(Employee employee) {
        em.persist(employee);
    }

    public List<Employee> findByRole(String role) {
        return em.createNativeQuery(
            "SELECT * FROM Employee WHERE properties ->> 'role' = :role", Employee.class)
            .setParameter("role", role)
            .getResultList();
    }

    public List<Employee> findAllEmployees() {
        return em.createQuery("SELECT e FROM Employee e", Employee.class)
                 .getResultList();
    }
}

The REST resource exposes the service:

@Path("/employees")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class EmployeeResource {
    @Inject
    private EmployeeService employeeService;

    @GET
    public Response getAllEmployees() {
        List<Employee> employees = employeeService.findAllEmployees();
        return Response.ok(employees).build();
    }

    @POST
    public Response createEmployee(Employee employee) {
        employeeService.insertEmployee(employee);
        return Response.ok("Employee created").build();
    }

    @GET
    @Path("/byrole")
    public List<Employee> findByRole(@QueryParam("role") String role) {
        return employeeService.findByRole(role);
    }
}

Configuration for WildFly

Maven dependencies needed are Jakarta EE 10 API, Hypersistence Utils, and Hibernate Core:

<dependencies>
    <dependency>
        <groupId>jakarta.platform</groupId>
        <artifactId>jakarta.jakartaee-api</artifactId>
        <version>10.0.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>io.hypersistence</groupId>
        <artifactId>hypersistence-utils-hibernate-63</artifactId>
        <version>3.7.0</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>6.4.1.Final</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Enable the org.hibernate.commons-annotations module in WEB-INF/jboss-deployment-structure.xml:

<jboss-deployment-structure>
    <deployment>
        <dependencies>
            <module name="org.hibernate.commons-annotations"/>
        </dependencies>
    </deployment>
</jboss-deployment-structure>

Define a persistence unit in resources/META-INF/persistence.xml pointing to the WildFly datasource:

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    version="2.1"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="my-persistence-unit">
        <description>Sample Hibernate PostgreSQL Dialect</description>
        <jta-data-source>java:/PostGreDS</jta-data-source>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
            <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
        </properties>
    </persistence-unit>
</persistence>

Testing the Application

Use curl to create an employee:

curl -X POST -H "Content-Type: application/json" -d '{
  "name": "Alice",
  "surname": "Smith",
  "properties": {
    "department": "HR",
    "role": "Manager"
  }
}' http://localhost:8080/postgres-json/employees

The response confirms creation. Then retrieve employees with role "Manager":

curl http://localhost:8080/postgres-json/employees/byrole?role=Manager

Both commands succeed, demonstrating that JSON keys can be used to filter rows.

Employee creation result
Employee creation result
Query result for role Manager
Query result for role Manager

Conclusion

PostgreSQL’s native json and jsonb types enable efficient storage and querying of JSON data. Combined with JPA’s entity mapping and native query capabilities—augmented by Hypersistence’s JsonType —developers can achieve seamless integration between Java objects and JSON documents in a relational database.

JSONPostgreSQLrestHibernatejpaJSONBHypersistence
JakartaEE China Community
Written by

JakartaEE China Community

JakartaEE China Community, official website: jakarta.ee/zh/community/china; gitee.com/jakarta-ee-china; space.bilibili.com/518946941; reply "Join group" to get QR code

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.