I have a JPA entity object with following structure:
@Table(name="item_info")
class Item(){
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@Column(name="item_name")
private String itemName;
@Column(name="product_sku")
private String productSku;
@Column(name="item_json")
private String itemJsonString;
@Transient
private ItemJson itemJson;
//Getters and setters
}
The itemJsonString field contains a json string value such as '{"key1":"value1","key2":"value2"}'
And the itemJson field contains the corresponding object which maps to the json string.
I get this entity object from database as follows:
Item item = itemRepository.findOne(1L); // Returns item with id 1
Now, the itemJson field is null since it is a transient field. And I have to set it manually using Jackson's ObjectMapper as follows:
itemJson = objectMapper.readValue(item.getItemJsonString(), ItemJson.class);
How can I make it such that when I do itemRepository.findOne()
, it returns an Item object with the itemJson field mapped to the json String automatically?
The code is bellow. ... @Column(name = "meta_data", columnDefinition = "json") @Convert(attributeName = "data", converter = JsonToMapConverter. class) private Map<String, Object> metaData = new HashMap<>(); ...
We can easily convert JSON data into a map because the JSON format is essentially a key-value pair grouping and the map also stores data in key-value pairs. Let's understand how we can use both JACKSON and Gson libraries to convert JSON data into a Map.
The Jackson ObjectMapper can parse JSON from a string, stream or file, and create a Java object or object graph representing the parsed JSON. Parsing JSON into Java objects is also referred to as to deserialize Java objects from JSON. The Jackson ObjectMapper can also create JSON from Java objects.
Association mappings are one of the key features of JPA and Hibernate. They model the relationship between two database tables as attributes in your domain model. That allows you to easily navigate the associations in your domain model and JPQL or Criteria queries.
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.
Here is the full working version of AttributeConverter
+ JPA + Kotlin.
Entity Class
In my case, database was mysql (8.x), which supports JSON as the underlying data type for column definition, and we can apply a custom converter using @Convert
annotation.
@Entity
data class Monitor (
@Id
val id: Long? = null,
@Column(columnDefinition = "JSON")
@Convert(converter = AlertConverter::class)
var alerts: List<Alert> = emptyList(),
var active: Boolean = false
)
Converter Definition Attribute converter needs to specify the conversion mechanism from data to db and reverse. We are using Jackson to convert a java object into String format and vice versa.
@Converter(autoApply = true)
class AlertConverter : AttributeConverter<List<Alert>, String> {
private val objectMapper = ObjectMapper()
override fun convertToDatabaseColumn(data: List<Alert>?): String {
return if (data != null && !data.isEmpty())
objectMapper.writeValueAsString(data)
else ""
}
override fun convertToEntityAttribute(dbData: String?): List<Alert> {
if (StringUtils.isEmpty(dbData)) {
return emptyList()
}
return objectMapper.readValue(dbData, object : TypeReference<List<Alert>>() {})
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With