This worked with:
Windows 7
Oracle Java 1.8.0_66
MySql 5.7.10.0
Spring Boot 1.3.1.RELEASE
Steps:
- Create a POJO to represent the JSON object that will be saved to the column.
package com.cherryshoe.model; /* * Represents a "documents" database column that holds json document data */ public class JsonDocuments { private String docId; private String docName; public String getDocId() { return docId; } public void setDocId(String docId) { this.docId = docId; } public String getDocName() { return docName; } public void setDocName(String docName) { this.docName = docName; } public JsonDocuments() { super(); } public JsonDocuments(String docId, String docName) { super(); this.docId = docId; this.docName = docName; } @Override public int hashCode() { ... } @Override public boolean equals(Object obj) { ... } @Override public String toString() { return "JsonDocuments [docId=" + docId + ", docName=" + docName + "]"; } }
-
Create a Converter Class that implements AttributerConverter.
- Annotate the class with JPA converter annotation
- I need to save a list of documents so the type argument to AttributeConverter is List<JsonDocuments> vs JsonDocuments
- We are using the Jackson JSON library to serialize/deserialize POJO's to JSON and vice versa. ObjectMapper is thread safe so it is declared as a static variable in the JpaJsonDocumentsConverter class.
- Override convertToDatabaseColumn and convertToEntityAttribute methods with objectMapper writeValue and readValue calls
package com.cherryshoe.utils; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.persistence.AttributeConverter; import javax.persistence.Converter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.cherryshoe.model.JsonDocuments; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @Converter public class JpaJsonDocumentsConverter implements AttributeConverter<List<JsonDocuments>, String> { // ObjectMapper is thread safe private final static ObjectMapper objectMapper = new ObjectMapper(); private Logger log = LoggerFactory.getLogger(getClass()); @Override public String convertToDatabaseColumn(List<JsonDocuments> meta) { String jsonString = ""; try { log.debug("Start convertToDatabaseColumn"); // convert list of POJO to json jsonString = objectMapper.writeValueAsString(meta); log.debug("convertToDatabaseColumn" + jsonString); } catch (JsonProcessingException ex) { log.error(ex.getMessage()); } return jsonString; } @Override public List<JsonDocuments> convertToEntityAttribute(String dbData) { List<JsonDocuments> list = new ArrayList<JsonDocuments>(); try { log.debug("Start convertToEntityAttribute"); // convert json to list of POJO list = Arrays.asList(objectMapper.readValue(dbData, JsonDocuments[].class)); log.debug("JsonDocumentsConverter.convertToDatabaseColumn" + list); } catch (IOException ex) { log.error(ex.getMessage()); } return list; } }
-
The JPA Entity class that contains the JSON column needs to have the @Convert annotation for the documents column. NOTE: You could also have in the JpaJsonDocumentsConverter class added the autoapply attribute to the @Converter annotation and set that to true to have JPA apply this converter to all entity attributes of type List<JsonDocuments>.
@Entity @Table(name = "TABLE_WITH_DOCUMENTS") public class TableWithDocuments implements Serializable { /** * */ private static final long serialVersionUID = 3781459465416706159L; ... entity attributes @Convert(converter = JpaJsonDocumentsConverter.class) private List<JsonDocuments> documents; ... entity attributes }
- Verified with an integration test on the Spring Data JPA Repository class that uses this entity that these methods worked. I did notice that the documents column could not be null, so when you create the new TableWithDocuments record, the documents column had to be set as an empty JsonArray if there were no documents. Adding in null checks in the JpaJsonDocumentsConverter convertToDatabaseColumn and convertToEntityAttribute did not help, it was inside the JPA code that would error out.
nice explanation
ReplyDeleteThanks a lot, your post was great valor to me! : )
ReplyDeletethanks so much.
ReplyDelete