Map Json String Column of a JPA Entity to Java Object Automatically

Map JSON string column of a JPA entity to Java object automatically

Your best bet would be to implement a javax.persistence.Converter. It would look something like:

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

@Converter(autoApply = true)
public class LocalDateAttributeConverter implements AttributeConverter<ItemJson, String> {

@Override
public String convertToDatabaseColumn(ItemJson entityValue) {
if( entityValue == null )
return null;

ObjectMapper mapper = new ObjectMapper();

return mapper.writeValueAsString(entityValue);
}

@Override
public ItemJson convertToEntityAttribute(String databaseValue) {
if( databaseValue == null )
return null;

ObjectMapper mapper = new ObjectMapper();

return mapper.readValue(databaseValue, ItemJson.class);

}
}

I've used this with WildFly and didn't have to do anything except have it be in the war file I was deploying.

How to map a map JSON column to Java Object with JPA

You can use a JPA converter to map your Entity to the database.
Just add an annotation similar to this one to your params field:

@Convert(converter = JpaConverterJson.class)

and then create the class in a similar way (this converts a generic Object, you may want to specialize it):

@Converter(autoApply = true)
public class JpaConverterJson implements AttributeConverter<Object, String> {

private final static ObjectMapper objectMapper = new ObjectMapper();

@Override
public String convertToDatabaseColumn(Object meta) {
try {
return objectMapper.writeValueAsString(meta);
} catch (JsonProcessingException ex) {
return null;
// or throw an error
}
}

@Override
public Object convertToEntityAttribute(String dbData) {
try {
return objectMapper.readValue(dbData, Object.class);
} catch (IOException ex) {
// logger.error("Unexpected IOEx decoding json from database: " + dbData);
return null;
}
}

}

That's it: you can use this class to serialize any object to json in the table.

How to map a MySQL JSON column to a Java entity property using JPA and Hibernate

I prefer to do this way:

  • Creating converter (attribute converter) from Map to String and vice versa.
  • Using Map to map mysql JSON column type in domain (entity) class

The code is bellow.

JsonToMapConverted.java

@Converter
public class JsonToMapConverter
implements AttributeConverter<String, Map<String, Object>>
{
private static final Logger LOGGER = LoggerFactory.getLogger(JsonToMapConverter.class);

@Override
@SuppressWarnings("unchecked")
public Map<String, Object> convertToDatabaseColumn(String attribute)
{
if (attribute == null) {
return new HashMap<>();
}
try
{
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(attribute, HashMap.class);
}
catch (IOException e) {
LOGGER.error("Convert error while trying to convert string(JSON) to map data structure.");
}
return new HashMap<>();
}

@Override
public String convertToEntityAttribute(Map<String, Object> dbData)
{
try
{
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(dbData);
}
catch (JsonProcessingException e)
{
LOGGER.error("Could not convert map to json string.");
return null;
}
}
}

Part of domain (entity-mapping) class

...

@Column(name = "meta_data", columnDefinition = "json")
@Convert(attributeName = "data", converter = JsonToMapConverter.class)
private Map<String, Object> metaData = new HashMap<>();

...

This solution perfectly works for me.

How to map a JSON column with H2, JPA, and Hibernate

JSON support was added to H2 after the question was asked, with version 1.4.200 (2019-10-14).

However, you rarely need a JSON data type in a database. JSON essentially is just a potentially very long string, so you can use CLOB which is available on most databases.

You do need the JSON data type if you need an SQL function that operates on them, and then only if the database insists that its JSON functions operate on a JSON type instead of on a CLOB. Such functions tend to be database-dependent though.

Mapping JSON object to Hibernate entity

We were using such approach to simplify design and get rid of many dtos (we were abusing them too much). Basically, it worked for us.

However, in our REST model we were trying to do not expose other relations for an object as you can always create another REST resources to access them.

So we just put @JsonIgnore annotations to relations mappings like @OneToMany or @ManyToOnemaking them transient.

Another problem I see that if you still like to return these relations you would have to use Join.FETCH strategy for them or move transaction management higher so that transaction still exists when a response is serialized to JSON (Open Session In View Pattern).
On my opinion these two solutions are not so good.

Mapping PostgreSQL JSON column to a Hibernate entity property

See PgJDBC bug #265.

PostgreSQL is excessively, annoyingly strict about data type conversions. It won't implicitly cast text even to text-like values such as xml and json.

The strictly correct way to solve this problem is to write a custom Hibernate mapping type that uses the JDBC setObject method. This can be a fair bit of hassle, so you might just want to make PostgreSQL less strict by creating a weaker cast.

As noted by @markdsievers in the comments and this blog post, the original solution in this answer bypasses JSON validation. So it's not really what you want. It's safer to write:

CREATE OR REPLACE FUNCTION json_intext(text) RETURNS json AS $$
SELECT json_in($1::cstring);
$$ LANGUAGE SQL IMMUTABLE;

CREATE CAST (text AS json) WITH FUNCTION json_intext(text) AS IMPLICIT;

AS IMPLICIT tells PostgreSQL it can convert without being explicitly told to, allowing things like this to work:

regress=# CREATE TABLE jsontext(x json);
CREATE TABLE
regress=# PREPARE test(text) AS INSERT INTO jsontext(x) VALUES ($1);
PREPARE
regress=# EXECUTE test('{}')
INSERT 0 1

Thanks to @markdsievers for pointing out the issue.

Persisting a JSON Object using Hibernate and JPA

Maven dependency

The first thing you need to do is to set up the following Hibernate Types Maven dependency in your project pom.xml configuration file:

<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>

Domain model

Let's assume you have the following entity:

@Entity(name = "Book")
@Table(name = "book")
@TypeDef(
typeClass = JsonType.class,
defaultForType = JsonNode.class
)
public class Book {

@Id
@GeneratedValue
private Long id;

@NaturalId
private String isbn;

@Column(columnDefinition = "jsonb")
private JsonNode properties;

//Getters and setters omitted for brevity
}

Notice the @TypeDef is used to instruct Hibernate to map the JsonNode object using the JsonType offered by the Hibernate Types project.

Testing time

Now, if you save an entity:

Book book = new Book();
book.setIsbn( "978-9730228236" );
book.setProperties(
JacksonUtil.toJsonNode(
"{" +
" \"title\": \"High-Performance Java Persistence\"," +
" \"author\": \"Vlad Mihalcea\"," +
" \"publisher\": \"Amazon\"," +
" \"price\": 44.99" +
"}"
)
);

entityManager.persist( book );

Hibernate is going to generate the following SQL statement:

INSERT INTO
book
(
isbn,
properties,
id
)
VALUES
(
'978-9730228236',
'{"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99}',
1
)

And you can also load it back and modify it:

Session session = entityManager.unwrap( Session.class );

Book book = session
.bySimpleNaturalId( Book.class )
.load( "978-9730228236" );

LOGGER.info( "Book details: {}", book.getProperties() );

book.setProperties(
JacksonUtil.toJsonNode(
"{" +
" \"title\": \"High-Performance Java Persistence\"," +
" \"author\": \"Vlad Mihalcea\"," +
" \"publisher\": \"Amazon\"," +
" \"price\": 44.99," +
" \"url\": \"https://www.amazon.com/High-Performance-Java-Persistence-Vlad-Mihalcea/dp/973022823X/\"" +
"}"
)
);

Hibernate taking caare of the UPDATE statement for you:

SELECT  b.id AS id1_0_
FROM book b
WHERE b.isbn = '978-9730228236'

SELECT b.id AS id1_0_0_ ,
b.isbn AS isbn2_0_0_ ,
b.properties AS properti3_0_0_
FROM book b
WHERE b.id = 1

-- Book details: {"price":44.99,"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon"}

UPDATE
book
SET
properties = '{"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99,"url":"https://www.amazon.com/High-Performance-Java-Persistence-Vlad-Mihalcea/dp/973022823X/"}'
WHERE
id = 1


Related Topics



Leave a reply



Submit