I'm trying to understand better some deserialization errors I'm seeing in Jackson in a legacy application being converted to a React front-end with Spring back-end.
I see the error when multiple objects of the same class are referenced in the JSON but they have different elements due to fields being ignored with #JsonIgnoreProperties to eliminate infinite recursion. The Spring application will send JSON to the front-end application from the database. We may manipulate a few fields but keep the same basic structure and when we post it back, it says it can't deserialize it. Here's a simplified example.
File named Child.java
public class Child implements java.io.Serializable {
private String name;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Parent parent;
public Child() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
File name Parent.java
public class Parent implements java.io.Serializable {
private String name;
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#JsonIgnoreProperties("parent")
private Set<Child> children = new HashSet<Child>(0);
public Parent() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Child> getChildren() {
return children;
}
public void setChildren(Set<Child> children) {
this.children = children;
}
}
Controller (normally we would be saving to database here)
#ResponseBody
#RequestMapping(value = "/returnParent", method = RequestMethod.POST)
public ResponseEntity<Parent> returnParent(#RequestBody Child child) throws Exception {
return new ResponseEntity<Parent>(child.getParent(), HttpStatus.OK);
}
JSON posted
{
"name":"child1",
"parent":{
"name":"parent1",
"children":[
{
"name":"child1"
},
{
"name":"child2"
}
]
}
}
Error:
org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: No _valueDeserializer assigned; nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: No
_valueDeserializer assigned\n at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 7,
column: 24] (through reference chain:
...Child["parent"]->...Parent["children"]->java.util.HashSet[0]->...Child["name\
I know that if I add this
#JsonIgnoreProperties(value={"children"}, allowGetters=true)
private Parent parent;
to Child that it will suppress the error and ignore child.parent.children but what if we need to be able post back these nested structures? This feels like a bug that it can't read the same JSON it created.
I'm looking for the different ways to map an enum using JPA. I especially want to set the integer value of each enum entry and to save only the integer value.
#Entity
#Table(name = "AUTHORITY_")
public class Authority implements Serializable {
public enum Right {
READ(100), WRITE(200), EDITOR (300);
private int value;
Right(int value) { this.value = value; }
public int getValue() { return value; }
};
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "AUTHORITY_ID")
private Long id;
// the enum to map :
private Right right;
}
A simple solution is to use the Enumerated annotation with EnumType.ORDINAL:
#Column(name = "RIGHT")
#Enumerated(EnumType.ORDINAL)
private Right right;
But in this case JPA maps the enum index (0,1,2) and not the value I want (100,200,300).
Th two solutions I found do not seem simple...
First Solution
A solution, proposed here, uses #PrePersist and #PostLoad to convert the enum to an other field and mark the enum field as transient:
#Basic
private int intValueForAnEnum;
#PrePersist
void populateDBFields() {
intValueForAnEnum = right.getValue();
}
#PostLoad
void populateTransientFields() {
right = Right.valueOf(intValueForAnEnum);
}
Second Solution
The second solution proposed here proposed a generic conversion object, but still seems heavy and hibernate-oriented (#Type doesn't seem to exist in Java EE):
#Type(
type = "org.appfuse.tutorial.commons.hibernate.GenericEnumUserType",
parameters = {
#Parameter(
name = "enumClass",
value = "Authority$Right"),
#Parameter(
name = "identifierMethod",
value = "toInt"),
#Parameter(
name = "valueOfMethod",
value = "fromInt")
}
)
Is there any other solutions ?
I've several ideas in mind but I don't know if they exist in JPA:
use the setter and getter methods of right member of Authority Class when loading and saving the Authority object
an equivalent idea would be to tell JPA what are the methods of Right enum to convert enum to int and int to enum
Because I'm using Spring, is there any way to tell JPA to use a specific converter (RightEditor) ?
For versions earlier than JPA 2.1, JPA provides only two ways to deal with enums, by their name or by their ordinal. And the standard JPA doesn't support custom types. So:
If you want to do custom type conversions, you'll have to use a provider extension (with Hibernate UserType, EclipseLink Converter, etc). (the second solution). ~or~
You'll have to use the #PrePersist and #PostLoad trick (the first solution). ~or~
Annotate getter and setter taking and returning the int value ~or~
Use an integer attribute at the entity level and perform a translation in getters and setters.
I'll illustrate the latest option (this is a basic implementation, tweak it as required):
#Entity
#Table(name = "AUTHORITY_")
public class Authority implements Serializable {
public enum Right {
READ(100), WRITE(200), EDITOR (300);
private int value;
Right(int value) { this.value = value; }
public int getValue() { return value; }
public static Right parse(int id) {
Right right = null; // Default
for (Right item : Right.values()) {
if (item.getValue()==id) {
right = item;
break;
}
}
return right;
}
};
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "AUTHORITY_ID")
private Long id;
#Column(name = "RIGHT_ID")
private int rightId;
public Right getRight () {
return Right.parse(this.rightId);
}
public void setRight(Right right) {
this.rightId = right.getValue();
}
}
This is now possible with JPA 2.1:
#Column(name = "RIGHT")
#Enumerated(EnumType.STRING)
private Right right;
Further details:
https://dzone.com/articles/mapping-enums-done-right
http://www.thoughts-on-java.org/jpa-21-how-to-implement-type-converter/
From JPA 2.1 you can use AttributeConverter.
Create an enumerated class like so:
public enum NodeType {
ROOT("root-node"),
BRANCH("branch-node"),
LEAF("leaf-node");
private final String code;
private NodeType(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
And create a converter like this:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter(autoApply = true)
public class NodeTypeConverter implements AttributeConverter<NodeType, String> {
#Override
public String convertToDatabaseColumn(NodeType nodeType) {
return nodeType.getCode();
}
#Override
public NodeType convertToEntityAttribute(String dbData) {
for (NodeType nodeType : NodeType.values()) {
if (nodeType.getCode().equals(dbData)) {
return nodeType;
}
}
throw new IllegalArgumentException("Unknown database value:" + dbData);
}
}
On the entity you just need:
#Column(name = "node_type_code")
You luck with #Converter(autoApply = true) may vary by container but tested to work on Wildfly 8.1.0. If it doesn't work you can add #Convert(converter = NodeTypeConverter.class) on the entity class column.
The best approach would be to map a unique ID to each enum type, thus avoiding the pitfalls of ORDINAL and STRING. See this post which outlines 5 ways you can map an enum.
Taken from the link above:
1&2. Using #Enumerated
There are currently 2 ways you can map enums within your JPA entities using the #Enumerated annotation. Unfortunately both EnumType.STRING and EnumType.ORDINAL have their limitations.
If you use EnumType.String then renaming one of your enum types will cause your enum value to be out of sync with the values saved in the database. If you use EnumType.ORDINAL then deleting or reordering the types within your enum will cause the values saved in the database to map to the wrong enums types.
Both of these options are fragile. If the enum is modified without performing a database migration, you could jeopodise the integrity of your data.
3. Lifecycle Callbacks
A possible solution would to use the JPA lifecycle call back annotations, #PrePersist and #PostLoad. This feels quite ugly as you will now have two variables in your entity. One mapping the value stored in the database, and the other, the actual enum.
4. Mapping unique ID to each enum type
The preferred solution is to map your enum to a fixed value, or ID, defined within the enum. Mapping to predefined, fixed value makes your code more robust. Any modification to the order of the enums types, or the refactoring of the names, will not cause any adverse effects.
5. Using Java EE7 #Convert
If you are using JPA 2.1 you have the option to use the new #Convert annotation. This requires the creation of a converter class, annotated with #Converter, inside which you would define what values are saved into the database for each enum type. Within your entity you would then annotate your enum with #Convert.
My preference: (Number 4)
The reason why I prefer to define my ID's within the enum as oppose to using a converter, is good encapsulation. Only the enum type should know of its ID, and only the entity should know about how it maps the enum to the database.
See the original post for the code example.
The problem is, I think, that JPA was never incepted with the idea in mind that we could have a complex preexisting Schema already in place.
I think there are two main shortcomings resulting from this, specific to Enum:
The limitation of using name() and ordinal(). Why not just mark a getter with #Id, the way we do with #Entity?
Enum's have usually representation in the database to allow association with all sorts of metadata, including a proper name, a descriptive name, maybe something with localization etc. We need the easy of use of an Enum combined with the flexibility of an Entity.
Help my cause and vote on JPA_SPEC-47
Would this not be more elegant than using a #Converter to solve the problem?
// Note: this code won't work!!
// it is just a sample of how I *would* want it to work!
#Enumerated
public enum Language {
ENGLISH_US("en-US"),
ENGLISH_BRITISH("en-BR"),
FRENCH("fr"),
FRENCH_CANADIAN("fr-CA");
#ID
private String code;
#Column(name="DESCRIPTION")
private String description;
Language(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
}
My own solution to solve this kind of Enum JPA mapping is the following.
Step 1 - Write the following interface that we will use for all enums that we want to map to a db column:
public interface IDbValue<T extends java.io.Serializable> {
T getDbVal();
}
Step 2 - Implement a custom generic JPA converter as follows:
import javax.persistence.AttributeConverter;
public abstract class EnumDbValueConverter<T extends java.io.Serializable, E extends Enum<E> & IDbValue<T>>
implements AttributeConverter<E, T> {
private final Class<E> clazz;
public EnumDbValueConverter(Class<E> clazz){
this.clazz = clazz;
}
#Override
public T convertToDatabaseColumn(E attribute) {
if (attribute == null) {
return null;
}
return attribute.getDbVal();
}
#Override
public E convertToEntityAttribute(T dbData) {
if (dbData == null) {
return null;
}
for (E e : clazz.getEnumConstants()) {
if (dbData.equals(e.getDbVal())) {
return e;
}
}
// handle error as you prefer, for example, using slf4j:
// log.error("Unable to convert {} to enum {}.", dbData, clazz.getCanonicalName());
return null;
}
}
This class will convert the enum value E to a database field of type T (e.g. String) by using the getDbVal() on enum E, and vice versa.
Step 3 - Let the original enum implement the interface we defined in step 1:
public enum Right implements IDbValue<Integer> {
READ(100), WRITE(200), EDITOR (300);
private final Integer dbVal;
private Right(Integer dbVal) {
this.dbVal = dbVal;
}
#Override
public Integer getDbVal() {
return dbVal;
}
}
Step 4 - Extend the converter of step 2 for the Right enum of step 3:
public class RightConverter extends EnumDbValueConverter<Integer, Right> {
public RightConverter() {
super(Right.class);
}
}
Step 5 - The final step is to annotate the field in the entity as follows:
#Column(name = "RIGHT")
#Convert(converter = RightConverter.class)
private Right right;
Conclusion
IMHO this is the cleanest and most elegant solution if you have many enums to map and you want to use a particular field of the enum itself as mapping value.
For all others enums in your project that need similar mapping logic, you only have to repeat steps 3 to 5, that is:
implement the interface IDbValue on your enum;
extend the EnumDbValueConverter with only 3 lines of code (you may also do this within your entity to avoid creating a separated class);
annotate the enum attribute with #Convert from javax.persistence package.
Hope this helps.
Possibly close related code of Pascal
#Entity
#Table(name = "AUTHORITY_")
public class Authority implements Serializable {
public enum Right {
READ(100), WRITE(200), EDITOR(300);
private Integer value;
private Right(Integer value) {
this.value = value;
}
// Reverse lookup Right for getting a Key from it's values
private static final Map<Integer, Right> lookup = new HashMap<Integer, Right>();
static {
for (Right item : Right.values())
lookup.put(item.getValue(), item);
}
public Integer getValue() {
return value;
}
public static Right getKey(Integer value) {
return lookup.get(value);
}
};
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "AUTHORITY_ID")
private Long id;
#Column(name = "RIGHT_ID")
private Integer rightId;
public Right getRight() {
return Right.getKey(this.rightId);
}
public void setRight(Right right) {
this.rightId = right.getValue();
}
}
I would do the folowing:
Declare separetly the enum, in it´s own file:
public enum RightEnum {
READ(100), WRITE(200), EDITOR (300);
private int value;
private RightEnum (int value) { this.value = value; }
#Override
public static Etapa valueOf(Integer value){
for( RightEnum r : RightEnum .values() ){
if ( r.getValue().equals(value))
return r;
}
return null;//or throw exception
}
public int getValue() { return value; }
}
Declare a new JPA entity named Right
#Entity
public class Right{
#Id
private Integer id;
//FIElDS
// constructor
public Right(RightEnum rightEnum){
this.id = rightEnum.getValue();
}
public Right getInstance(RightEnum rightEnum){
return new Right(rightEnum);
}
}
You will also need a converter for receiving this values (JPA 2.1 only and there´s a problem I´ll not discuss here with these enum´s to be directly persisted using the converter, so it will be a one way road only)
import mypackage.RightEnum;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/**
*
*
*/
#Converter(autoApply = true)
public class RightEnumConverter implements AttributeConverter<RightEnum, Integer>{
#Override //this method shoudn´t be used, but I implemented anyway, just in case
public Integer convertToDatabaseColumn(RightEnum attribute) {
return attribute.getValue();
}
#Override
public RightEnum convertToEntityAttribute(Integer dbData) {
return RightEnum.valueOf(dbData);
}
}
The Authority entity:
#Entity
#Table(name = "AUTHORITY_")
public class Authority implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "AUTHORITY_ID")
private Long id;
// the **Entity** to map :
private Right right;
// the **Enum** to map (not to be persisted or updated) :
#Column(name="COLUMN1", insertable = false, updatable = false)
#Convert(converter = RightEnumConverter.class)
private RightEnum rightEnum;
}
By doing this way, you can´t set directly to the enum field. However, you can set the Right field in Authority using
autorithy.setRight( Right.getInstance( RightEnum.READ ) );//for example
And if you need to compare, you can use:
authority.getRight().equals( RightEnum.READ ); //for example
Which is pretty cool, I think. It´s not totally correct, since the converter it´s not intended to be use with enum´s. Actually, the documentation says to never use it for this purpose, you should use the #Enumerated annotation instead. The problem is that there are only two enum types: ORDINAL or STRING, but the ORDINAL is tricky and not safe.
However, if it doesn´t satisfy you, you can do something a little more hacky and simpler (or not).
Let´s see.
The RightEnum:
public enum RightEnum {
READ(100), WRITE(200), EDITOR (300);
private int value;
private RightEnum (int value) {
try {
this.value= value;
final Field field = this.getClass().getSuperclass().getDeclaredField("ordinal");
field.setAccessible(true);
field.set(this, value);
} catch (Exception e) {//or use more multicatch if you use JDK 1.7+
throw new RuntimeException(e);
}
}
#Override
public static Etapa valueOf(Integer value){
for( RightEnum r : RightEnum .values() ){
if ( r.getValue().equals(value))
return r;
}
return null;//or throw exception
}
public int getValue() { return value; }
}
and the Authority entity
#Entity
#Table(name = "AUTHORITY_")
public class Authority implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "AUTHORITY_ID")
private Long id;
// the **Enum** to map (to be persisted or updated) :
#Column(name="COLUMN1")
#Enumerated(EnumType.ORDINAL)
private RightEnum rightEnum;
}
In this second idea, its not a perfect situation since we hack the ordinal attribute, but it´s a much smaller coding.
I think that the JPA specification should include the EnumType.ID where the enum value field should be annotated with some kind of #EnumId annotation.
I have two classes with a redundant shared attribute:
class Parent {
int version;
Child child;
}
class Child {
int version;
String name;
}
The version field from Child is just a local copy of the Parent's own field, so it is represented like this in JSON:
{
"version": 2,
"child": {
"name": "john"
}
}
I want to deserialize this JSON payload to the corresponding Parent object with parent.child.version == 2 (the value is copied from the parent). How can I achieve this?
I thought about using #JacksonInject to pass the version to the Child, but that requires populating the injected values while deserializing the parent object. I could not find any way to register an injected value in a custom deserializer: the findInjectableValue method of DeserializationContext only lets us look up such a value.
It's not really a Jackson-specific solution but it's probably the simplest one: create a PreChild class whose attributes match the JSON payload, then convert the PreChild to a Child in the constructor of Parent.
class PreChild {
String name;
public Child withVersion(int version) {
return Child(version, name);
}
}
class Parent {
int version;
Child child;
#JsonCreator
Parent(
#JsonProperty("version") int version,
#JsonProperty("child") PreChild preChild) {
this.version = version;
this.child = preChild.withVersion(version);
}
}
I need to serialize an entity with only two column when it's called by a foreign key. I'am working in Wildfly, so I'am searching for a jackson solutions.
Suppose I have entity class A
public class A{
private Long id;
private String name;
private String anotherinfo;
private String foo;
...
}
and another class B:
public class B{
private Long id;
private String name;
private A parent;
}
I want to serialize A with all his field when i search for A, but when i need to retrieve an istance of B, i need only two field (an ID and a label)
If I use annotations:
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#JsonIdentityReference(alwaysAsId=true)
private A parent;
i'll return only the id.
The result i want will be like:
B: {
"id" : 1,
"name" : "test",
"parent" : {
"id" : 1,
"name" : 2
}
}
You can use the JsonIgnoreProperties annotation, to disable specific fields for serialization (and deserialization):
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
public class B {
private Long id;
private String name;
#JsonIgnoreProperties({"anotherinfo", "foo"})
private A parent;
Have A extend another class, say C:
class C {
Long id;
String name;
}
class A extends C {
String anotherinfo;
String foo;
...
}
Then, in B:
class B {
Long id;
String name;
#JsonSerialize(as=C.class)
A parent;
}
When you serialize B, its parent field will have just the fields from C, but everywhere else that you serialize an A object you will see all the fields from both A and C.
For more information, take a look at https://github.com/FasterXML/jackson-annotations#annotations-for-choosing-moreless-specific-types
Solved adding a Json Serializer.
I have created an NationJsonSerializer for the parent class:
public class NationJsonSerializer extends JsonSerializer<TNation> {
#Override
public void serialize(TNation value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeNumberField("id", value.getId());
jgen.writeStringField("name", value.getComune());
jgen.writeStringField("iso", value.getCap());
jgen.writeEndObject();
}
}
Then,in the city class, i put the annotation
#JoinColumn(name = "idtnation",referencedColumnName = "id",nullable = true)
#ManyToOne(targetEntity = TNation.class, fetch = FetchType.EAGER)
#JsonSerialize(using = NationJsonSerializer.class)
private TNation nation;
So, if I use a method Nation n = getNation(long id); i'll receive all columns, but if i use getCity(), I'll receive a simplified version.
I am writing a webservice to maintain a database. I am trying to use JPA (EclipseLink) for the entity classes. However, the database uses natural primary keys and therefore there's potential that an update on the ID fields will fail due to foreign key constraints. Our DBA has provided a function to update the ID fields which will create a new parent record with the updated ID, update the child records to point to the new parent and delete the old parent.
If the ID fields could be updated "normally", I would have a situation like this:
#Entity
#Table(name = "PARENT")
public class Parent implements Serializable
{
private static final long serialVersionUID = 1L;
private String parent;
private String attribute;
private Set<Child> childs;
public Parent()
{
}
#Id
#Column(name = "PARENT")
public String getParent()
{
return this.parent;
}
public void setParent(String parent)
{
this.parent = parent;
}
#Column(name = "ATTRIBUTE")
public String getAttribute()
{
return this.attribute;
}
public void setAttribute(String attribute)
{
this.attribute = attribute;
}
#OneToMany(mappedBy = "parentBean")
public Set<Child> getChilds()
{
return this.childs;
}
public void setChilds(Set<Child> childs)
{
this.childs = childs;
}
}
#Entity
#Table(name = "CHILD")
public class Child implements Serializable
{
private static final long serialVersionUID = 1L;
private String child;
private String attribute;
private Parent parentBean;
public Child()
{
}
#Id
#Column(name = "CHILD")
public String getChild()
{
return this.child;
}
public void setChild(String child)
{
this.child = child;
}
#Column(name = "ATTRIBUTE")
public String getAttribute()
{
return this.attribute;
}
public void setAttribute(String attribute)
{
this.attribute = attribute;
}
#ManyToOne
#JoinColumn(name = "PARENT")
public Parent getParent()
{
return this.parent;
}
public void setParent(Parent parent)
{
this.parent = parent;
}
}
I also have a GenericServiceBean class with a method to call functions:
#Stateless
public class GenericServiceBean implements GenericService
{
#PersistenceContext(unitName = "PersistenceUnit")
EntityManager em;
public GenericServiceBean()
{
// empty
}
#Override
public <T> T create(T t)
{
em.persist(t);
return t;
}
#Override
public <T> void delete(T t)
{
t = em.merge(t);
em.remove(t);
}
#Override
public <T> T update(T t)
{
return em.merge(t);
}
#Override
public <T> T find(Class<T> type, Object id)
{
return em.find(type, id);
}
. . .
#Override
public String executeStoredFunctionWithNamedArguments(String functionName,
LinkedHashMap<String, String> namedArguments)
{
Session session = JpaHelper.getEntityManager(em).getServerSession();
StoredFunctionCall functionCall = new StoredFunctionCall();
functionCall.setProcedureName(functionName);
functionCall.setResult("RESULT", String.class);
for (String key : namedArguments.keySet())
{
functionCall.addNamedArgumentValue(key, namedArguments.get(key));
}
ValueReadQuery query = new ValueReadQuery();
query.setCall(functionCall);
String status = (String)session.executeQuery(query);
return status;
}
}
If I set the ID fields to be not editable:
#Id
#Column(name = "PARENT", udpatable=false)
public String getParent()
{
return this.parent;
}
and call parent.setParent(newParent) will this still update the ID in the entity object? How does this affect any child entities? Will they also be updated (or not)?
Another scenario I don't know how to deal with is where I need to update both the ID and another attribute. Should I call the function which updates (and commits) the ID in the database then make calls to set both the ID and attribute via the normal set* methods and then the persistence context will only commit the attribute change?
Perhaps this is a situation where JPA is not appropriate?
Any advice on this is greatly appreciated.
If I set the ID fields to be not editable (...) and call parent.setParent(newParent) will this still update the ID in the entity object? How does this affect any child entities? Will they also be updated (or not)?
updatable=false means that the column won't be part of the SQL UPDATE statement regardless of what you do at the object level so the Id shouldn't be updated. And I'm also tempted to say that child entities shouldn't be affected, especially since you're not cascading anything.
Another scenario I don't know how to deal with is where I need to update both the ID and another attribute (...)
Well, my understanding is that you'd have to call the function anyway so I would call it first.
Perhaps this is a situation where JPA is not appropriate?
I'm not sure raw SQL would deal better with your situation. Actually, the whole idea of changing primary keys sounds strange if I may.