Sometimes we don’t want to serialize a JSONObject into a plain old java object (POJO), as it may be a complex JSON resulting in multiple classes, or because we simply want to store json in our relational database, and we might just use a String to persist it, but how to store a given JSONObject using JPA @Entity in Postgresql or MySql or any other using JPA with hibernate, and avoiding refactoring our JSONObject references to return String so we can store it?

JPA entity with JSONObject

Let’s start by defining a simple @Entity

all examples given make use of Lombok

User.java

@Entity
@Data
@Table(name = "users")
public class User {
    @Id
    private Long id;

    @NonNull
    private String username;

    @NonNull
    private JSONObject jsonData;
}

If we use the Entity as defined above we will end up with a similar error to the one below

 Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.MappingException: Could not determine type for: org.json.JSONObject

So, we are unable to directly use JSONObject, or JSONArray for example as an object for a column using JPA with hibernate directly.

Using @Converter Annotation with a Converter class

@Converter JPA annotation to the rescue, in order to grant us attribute conversion so we can go and keep using our entity object with JSONObject.

First of all, in order to use the @Converter annotation, we are required to actually create a converter class, that will be responsible for returning a persistable Type with JPA, while allowing the interaction with the Entity Object using our JSONObject (or JSONArray) class, allowing us to return table entries with a JSONObject that we are able to obtain data directly from json text, without converting string to JSONObject all over our code.

JSONObjectConverter.java

package com.ilhicas.converters;

import org.json.JSONArray;
import org.json.JSONException;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter
public class JSONObjectConverter implements AttributeConverter<JSONObject, String> {
    @Override
    public String convertToDatabaseColumn(JSONObject jsonData) {
        String json;
        try{
            json = jsonData.toString();
        }
        catch (NullPointerException ex)
        {
            //extend error handling here if you want
            json = "";
        }
        return json;
    }

    @Override
    public JSONObject convertToEntityAttribute(String jsonDataAsJson) {
        JSONObject jsonData;
        try {
            jsonData = new JSONObject(jsonDataAsJson);
        } catch (JSONException ex) {
            //extend error handling here if you want
            jsonData = null;
        }
        return jsonData;
    }
}

So what exactly is the above class doing? It implements the AttributeConverter.class which is responsible for taking an object, when persisting, and return a known persistable Object (such as a string in our case), while retrieving a string and converting it to a custom Object we use, a bit like JSON marshall and unmashalling.

So now that we have our Converter class, we may use it along with our @Converter annotation.

@Entity
@Data
@Table(name = "users")
public class User {
    @Id
    private Long id;

    @NonNull
    private String username;

    @NonNull
    @Column(columnDefinition = "TEXT")
    @Convert(converter= JSONObjectConverter.class)
    private JSONObject jsonData;
}

We also added @Column(columnDefinition = "TEXT") , or could as well use @LOB given large text entries in json.

And that’s it, we may now interact with our User class using JSONObject directly

Below is just an example, and repository and autowired is a simple way to reference our entity manager if you would be using @Springboot for example

Example.java

...
@Autowired
private UserRepository userRepository;

public void someMethod()
{
    User example = userRepository.findByUsername("ilhicas");
    System.out.println(example.getJsonData().getString("KEY") )
    //prints out value for key
    
    JSONObject toSet = new JSONObject();
    toSet.put("SOME_OTHER_KEY", "SOME_VALUE")
    example.setJsonData(toSet);
    userRepository.save(example);
    //Saves our user uson jsonData from our JSONObject
}
...

And that’s it, thank you for reading, hope it helped you.

Remember, we are converting JSONObject, but we could be converting any kind of custom Classes.

If it helped, bookmark or share it with others who might face similar issues.

André Ilhicas dos Santos

Devops Padawan, curious about systems automation, learning new languages, paradigms tools each day.

ilhicas ilhicas


Published