When trying to convert a JPA object that has a bi-directional association into JSON, I keep getting
org.codehaus.jackson.map.JsonMappingException: Infinite
You can now use JsonIgnoreProperties to suppress serialization of properties (during serialization), or ignore processing of JSON properties read (during deserialization). If this is not what you're looking for, please keep reading below.
(Thanks to As Zammel AlaaEddine for pointing this out).
Since Jackson 1.6 you can use two annotations to solve the infinite recursion problem without ignoring the getters/setters during serialization: @JsonManagedReference and @JsonBackReference.
Explanation
For Jackson to work well, one of the two sides of the relationship should not be serialized, in order to avoid the infite loop that causes your stackoverflow error.
So, Jackson takes the forward part of the reference (your Set<BodyStat> bodyStats
in Trainee class), and converts it in a json-like storage format; this is the so-called marshalling process. Then, Jackson looks for the back part of the reference (i.e. Trainee trainee
in BodyStat class) and leaves it as it is, not serializing it. This part of the relationship will be re-constructed during the deserialization (unmarshalling) of the forward reference.
You can change your code like this (I skip the useless parts):
Business Object 1:
@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class Trainee extends BusinessObject {
@OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Column(nullable = true)
@JsonManagedReference
private Set<BodyStat> bodyStats;
Business Object 2:
@Entity
@Table(name = "ta_bodystat", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name="trainee_fk")
@JsonBackReference
private Trainee trainee;
Now it all should work properly.
If you want more informations, I wrote an article about Json and Jackson Stackoverflow issues on Keenformatics, my blog.
EDIT:
Another useful annotation you could check is @JsonIdentityInfo: using it, everytime Jackson serializes your object, it will add an ID (or another attribute of your choose) to it, so that it won't entirely "scan" it again everytime. This can be useful when you've got a chain loop between more interrelated objects (for example: Order -> OrderLine -> User -> Order and over again).
In this case you've got to be careful, since you could need to read your object's attributes more than once (for example in a products list with more products that share the same seller), and this annotation prevents you to do so. I suggest to always take a look at firebug logs to check the Json response and see what's going on in your code.
Sources:
You Should use @JsonBackReference with @ManyToOne entity and @JsonManagedReference with @onetomany containing entity classes.
@OneToMany(
mappedBy = "queue_group",fetch = FetchType.LAZY,
cascade = CascadeType.ALL
)
@JsonManagedReference
private Set<Queue> queues;
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name = "qid")
// @JsonIgnore
@JsonBackReference
private Queue_group queue_group;
In case you are using Spring Data Rest, issue can be resolved by creating Repositories for every Entity involved in cyclical references.
I have the same problem after doing more analysis i came to know that, we can get mapped entity also by just keeping @JsonBackReference at OneToMany annotation
@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class Trainee extends BusinessObject {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name = "id", nullable = false)
private Integer id;
@Column(name = "name", nullable = true)
private String name;
@Column(name = "surname", nullable = true)
private String surname;
@OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Column(nullable = true)
@JsonBackReference
private Set<BodyStat> bodyStats;
Working fine for me Resolve Json Infinite Recursion problem when working with Jackson
This is what I have done in oneToMany and ManyToOne Mapping
@ManyToOne
@JoinColumn(name="Key")
@JsonBackReference
private LgcyIsp Key;
@OneToMany(mappedBy="LgcyIsp ")
@JsonManagedReference
private List<Safety> safety;
I'm a late comer and it's such a long thread already. But I spent a couple of hours trying to figure this out too, and would like to give my case as another example.
I tried both JsonIgnore, JsonIgnoreProperties and BackReference solutions, but strangely enough it was like they weren't picked up.
I used Lombok and thought that maybe it interferes, since it creates constructors and overrides toString (saw toString in stackoverflowerror stack).
Finally it wasn't Lombok's fault - I used automatic NetBeans generation of JPA entities from database tables, without giving it much thought - well, and one of the annotations that were added to the generated classes was @XmlRootElement. Once I removed it everything started working. Oh well.