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.
How to save json Object in postgresql using Hibernate in java?
Create a table in postgres
CREATE TABLE demoJson(
id SERIAL NOT NULL PRIMARY KEY,
jsonData JSONB NOT NULL
);
I created a hibernate entity class as DemoJsonEnitity and defined @TypeDefs
@Entity
@Table(name = "demoJson")
@TypeDefs({
@TypeDef(name = "json", typeClass = JsonStringType.class)
,
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
public class DemoJsonEnitity implements Serializable {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id")
private int dataId;
@Type(type = "jsonb")
@Column(name = "jsonData", columnDefinition = "jsonb")
private String jsonData;
public int getDataId() {
return dataId;
}
public void setDataId(int dataId) {
this.dataId = dataId;
}
public String getJsonData() {
return jsonData;
}
public void setJsonData(String jsonData) {
this.jsonData = jsonData;
}
}
And thats it. So easy, I used this dependency
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.9.11</version>
</dependency>
To check I tried saving data as
public static void main(String arg[]) {
try {
DemoJsonEnitity obj = new DemoJsonEnitity();
JSONObject jsonObj = new JSONObject();
Map m1 = new LinkedHashMap(2);
m1.put("oldValue", "Active");
m1.put("newValue", "InActive");
jsonObj.put("status", m1);
Map m2 = new LinkedHashMap(2);
m2.put("oldValue", "Test 6");
m2.put("newValue", "Test 6 updated");
jsonObj.put("taskDetails", m2);
obj.setJsonData(jsonObj.toString());
Session session = null;
Transaction tx = null;
try {
session = sf.openSession();
tx = session.beginTransaction();
session.save(obj);
tx.commit();
} catch (Exception e) {
if (tx != null) {
tx.rollback();
}
throw new RuntimeException(e);
} finally {
session.close();
}
} catch (Exception e) {
System.out.println("error: " + e.getMessage());
}
}
Output
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.
Writing to JSON column of Postgres database using Spring / JPA
You'll need to either use setObject
at the JDBC level, or pass the PgJDBC parameter stringtype=unspecified
to allow implicit casts from string types to json
etc.
It's a problem with PostgreSQL being too strict about type casting.
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.
Related Topics
Selenium 2.53 Not Working on Firefox 47
How to Avoid Floating Point Precision Errors with Floats or Doubles in Java
Design Patterns: Factory VS Factory Method VS Abstract Factory
Can't Read and Write a Tiff Image File Using Java Imageio Standard Library
How to Fix Role in Spring Security
Comparing Java Enum Members: == or Equals()
Converting Between Java.Time.Localdatetime and Java.Util.Date
Url to Load Resources from the Classpath in Java
Java - Convert Integer to String
Convert a JSON String to a Hashmap
Delete Directories Recursively in Java
Java Remove Duplicates from an Array
Strange Floating-Point Behaviour in a Java Program
Adding Header for Httpurlconnection
How Does a for Loop Work, Specifically For(;;)
How to Create a Temporary Directory/Folder in Java
Copy a Stream to Avoid "Stream Has Already Been Operated Upon or Closed"