JsonMappingException: Infinite recursion (StackOverflowError) in Spring Boot GET request [duplicate] - java

When trying to convert a JPA object that has a bi-directional association into JSON, I keep getting
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
All I found is this thread which basically concludes with recommending to avoid bi-directional associations. Does anyone have an idea for a workaround for this spring bug?
------ EDIT 2010-07-24 16:26:22 -------
Codesnippets:
Business Object 1:
#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)
private Set<BodyStat> bodyStats;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<Training> trainings;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<ExerciseType> exerciseTypes;
public Trainee() {
super();
}
//... getters/setters ...
}
Business Object 2:
import javax.persistence.*;
import java.util.Date;
#Entity
#Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "height", nullable = true)
private Float height;
#Column(name = "measuretime", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date measureTime;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="trainee_fk")
private Trainee trainee;
}
Controller:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
#Controller
#RequestMapping(value = "/trainees")
public class TraineesController {
final Logger logger = LoggerFactory.getLogger(TraineesController.class);
private Map<Long, Trainee> trainees = new ConcurrentHashMap<Long, Trainee>();
#Autowired
private ITraineeDAO traineeDAO;
/**
* Return json repres. of all trainees
*/
#RequestMapping(value = "/getAllTrainees", method = RequestMethod.GET)
#ResponseBody
public Collection getAllTrainees() {
Collection allTrainees = this.traineeDAO.getAll();
this.logger.debug("A total of " + allTrainees.size() + " trainees was read from db");
return allTrainees;
}
}
JPA-implementation of the trainee DAO:
#Repository
#Transactional
public class TraineeDAO implements ITraineeDAO {
#PersistenceContext
private EntityManager em;
#Transactional
public Trainee save(Trainee trainee) {
em.persist(trainee);
return trainee;
}
#Transactional(readOnly = true)
public Collection getAll() {
return (Collection) em.createQuery("SELECT t FROM Trainee t").getResultList();
}
}
persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="RDBMS" transaction-type="RESOURCE_LOCAL">
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.hbm2ddl.auto" value="validate"/>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<!-- <property name="dialect" value="org.hibernate.dialect.HSQLDialect"/> -->
</properties>
</persistence-unit>
</persistence>

JsonIgnoreProperties [2017 Update]:
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).
JsonManagedReference and JsonBackReference
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:
Keenformatics - How To Solve JSON infinite recursion Stackoverflow (my blog)
Jackson References
Personal experience

You may use #JsonIgnore to break the cycle (reference).
You need to import org.codehaus.jackson.annotate.JsonIgnore (legacy versions) or com.fasterxml.jackson.annotation.JsonIgnore (current versions).

The new annotation #JsonIgnoreProperties resolves many of the issues with the other options.
#Entity
public class Material{
...
#JsonIgnoreProperties("costMaterials")
private List<Supplier> costSuppliers = new ArrayList<>();
...
}
#Entity
public class Supplier{
...
#JsonIgnoreProperties("costSuppliers")
private List<Material> costMaterials = new ArrayList<>();
....
}
Check it out here. It works just like in the documentation:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html

Also, using Jackson 2.0+ you can use #JsonIdentityInfo. This worked much better for my hibernate classes than #JsonBackReference and #JsonManagedReference, which had problems for me and did not solve the issue. Just add something like:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#traineeId")
public class Trainee extends BusinessObject {
#Entity
#Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#bodyStatId")
public class BodyStat extends BusinessObject {
and it should work.

Also, Jackson 1.6 has support for handling bi-directional references... which seems like
what you are looking for (this blog entry also mentions the feature)
And as of July 2011, there is also "jackson-module-hibernate" which might help in some aspects of dealing with Hibernate objects, although not necessarily this particular one (which does require annotations).

This worked perfectly fine for me.
Add the annotation #JsonIgnore on the child class where you mention the reference to the parent class.
#ManyToOne
#JoinColumn(name = "ID", nullable = false, updatable = false)
#JsonIgnore
private Member member;

Now Jackson supports avoiding cycles without ignoring the fields:
Jackson - serialization of entities with birectional relationships (avoiding cycles)

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;

For me the best solution is to use #JsonView and create specific filters for each scenario. You could also use #JsonManagedReference and #JsonBackReference, however it is a hardcoded solution to only one situation, where the owner always references the owning side, and never the opposite. If you have another serialization scenario where you need to re-annotate the attribute differently, you will not be able to.
Problem
Lets use two classes, Company and Employee where you have a cyclic dependency between them:
public class Company {
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
public class Employee {
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
And the test class that tries to serialize using ObjectMapper (Spring Boot):
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
public class CompanyTest {
#Autowired
public ObjectMapper mapper;
#Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
String jsonCompany = mapper.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
If you run this code, you'll get the:
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
Solution Using `#JsonView`
#JsonView enables you to use filters and choose what fields should be included while serializing the objects. A filter is just a class reference used as a identifier. So let's first create the filters:
public class Filter {
public static interface EmployeeData {};
public static interface CompanyData extends EmployeeData {};
}
Remember, the filters are dummy classes, just used for specifying the fields with the #JsonView annotation, so you can create as many as you want and need. Let's see it in action, but first we need to annotate our Company class:
public class Company {
#JsonView(Filter.CompanyData.class)
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
and change the Test in order for the serializer to use the View:
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
public class CompanyTest {
#Autowired
public ObjectMapper mapper;
#Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
ObjectWriter writter = mapper.writerWithView(Filter.CompanyData.class);
String jsonCompany = writter.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
Now if you run this code, the Infinite Recursion problem is solved, because you have explicitly said that you just want to serialize the attributes that were annotated with #JsonView(Filter.CompanyData.class).
When it reaches the back reference for company in the Employee, it checks that it's not annotated and ignore the serialization. You also have a powerful and flexible solution to choose which data you want to send through your REST APIs.
With Spring you can annotate your REST Controllers methods with the desired #JsonView filter and the serialization is applied transparently to the returning object.
Here are the imports used in case you need to check:
import static org.junit.Assert.assertTrue;
import javax.transaction.Transactional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.annotation.JsonView;

There's now a Jackson module (for Jackson 2) specifically designed to handle Hibernate lazy initialization problems when serializing.
https://github.com/FasterXML/jackson-datatype-hibernate
Just add the dependency (note there are different dependencies for Hibernate 3 and Hibernate 4):
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.4.0</version>
</dependency>
and then register the module when intializing Jackson's ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate4Module());
Documentation currently isn't great. See the Hibernate4Module code for available options.

#JsonIgnoreProperties is the answer.
Use something like this ::
#OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
#JsonIgnoreProperties("course")
private Set<Student> students;

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 my case it was enough to change relation from:
#OneToMany(mappedBy = "county")
private List<Town> towns;
to:
#OneToMany
private List<Town> towns;
another relation stayed as it was:
#ManyToOne
#JoinColumn(name = "county_id")
private County county;

I also met the same problem. I used #JsonIdentityInfo's ObjectIdGenerators.PropertyGenerator.class generator type.
That's my solution:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Trainee extends BusinessObject {
...

VERY IMPORTANT: If you are using LOMBOK, make shure to exclude attributes of collections like Set, List, etc...
Like this:
#EqualsAndHashCode(exclude = {"attributeOfTypeList", "attributeOfTypeSet"})

Be sure you use com.fasterxml.jackson everywhere. I spent much time to find it out.
<properties>
<fasterxml.jackson.version>2.9.2</fasterxml.jackson.version>
</properties>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${fasterxml.jackson.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${fasterxml.jackson.version}</version>
</dependency>
Then use #JsonManagedReference and #JsonBackReference.
Finally, you can serialize your model to JSON:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(model);

You can use #JsonIgnore, but this will ignore the json data which can be accessed because of the Foreign Key relationship. Therefore if you reqiure the foreign key data (most of the time we require), then #JsonIgnore will not help you. In such situation please follow the below solution.
you are getting Infinite recursion, because of the BodyStat class again referring the Trainee object
BodyStat
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="trainee_fk")
private Trainee trainee;
Trainee
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<BodyStat> bodyStats;
Therefore, you have to comment/omit the above part in Trainee

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;

you can use DTO pattern
create class TraineeDTO without any anotation hiberbnate and you can use jackson mapper to convert Trainee to TraineeDTO and bingo the error message disapeare :)

If you cannot ignore the property, try modifying the visibility of the field. In our case, we had old code still submitting entities with the relationship, so in my case, this was the fix:
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Trainee trainee;

For some reason, in my case, it wasn't working with Set. I had to change it to List and use #JsonIgnore and #ToString.Exclude to get it working.
Replace Set with List:
//before
#OneToMany(mappedBy="client")
private Set<address> addressess;
//after
#OneToMany(mappedBy="client")
private List<address> addressess;
And add #JsonIgnore and #ToString.Exclude annotations:
#ManyToOne
#JoinColumn(name="client_id", nullable = false)
#JsonIgnore
#ToString.Exclude
private Client client;

If you use #JsonManagedReference, #JsonBackReference or #JsonIgnore annotation it ignore some fields and solve Infinite Recursion with Jackson JSON.
But if you use #JsonIdentityInfo which also avoid the Infinite Recursion and you can get all the fields values, so I suggest that you use #JsonIdentityInfo annotation.
#JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator.class, property="#id")
Refer this article https://www.toptal.com/javascript/bidirectional-relationship-in-json to get good understanding about #JsonIdentityInfo annotation.

This post: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
has a full explanation.
If you are using Jackson with older versions, you can try #jsonmanagedreference + #jsonbackreference. If your Jackson is above 2 (1.9 also doesn't work as I know), try #JsonIdentityInfo instead.

As someone using Spring Data and Lombok, this is how I solved it for myself.
#Entity
#Data
public class Foo extends BaseEntity {
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "foo_id")
#JsonIgnoreProperties("parent_foo")
#EqualsAndHashCode.Exclude
private Set<Bar> linkedBars;
}
#Entity
#Data
public class Bar extends BaseEntity {
#Column(name = "foo_id")
private Long parentFooId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "foo_id", insertable = false, updatable = false)
#JsonIgnoreProperties({"linkedBars"})
private Foo parentFoo;
}
The JsonIgnoreProperties annotation stops infinite recursion as many answers have discussed above.
#EqualsAndHashCode.Exclude prevents the StackOverflowError caused by hashCode and equals being called recursively.
Using Set over List resolves the MultipleBagFetchException which occurs when you add multiple collection fields. You can also use #Fetch(value = FetchMode.SUBSELECT) to avoid the cartesian product, but I haven't tried it personally since my use case didn't need it.
The explicit definition of parentFooId in Bar is to allow mapping Foo entities with Bars.

I had this problem, but I didn't want to use annotation in my entities, so I solved by creating a constructor for my class, this constructor must not have a reference back to the entities who references this entity. Let's say this scenario.
public class A{
private int id;
private String code;
private String name;
private List<B> bs;
}
public class B{
private int id;
private String code;
private String name;
private A a;
}
If you try to send to the view the class B or A with #ResponseBody it may cause an infinite loop. You can write a constructor in your class and create a query with your entityManager like this.
"select new A(id, code, name) from A"
This is the class with the constructor.
public class A{
private int id;
private String code;
private String name;
private List<B> bs;
public A(){
}
public A(int id, String code, String name){
this.id = id;
this.code = code;
this.name = name;
}
}
However, there are some constrictions about this solution, as you can see, in the constructor I did not make a reference to List bs this is because Hibernate does not allow it, at least in version 3.6.10.Final, so when I need to show both entities in a view I do the following.
public A getAById(int id); //THE A id
public List<B> getBsByAId(int idA); //the A id.
The other problem with this solution, is that if you add or remove a property you must update your constructor and all your queries.

In case you are using Spring Data Rest, issue can be resolved by creating Repositories for every Entity involved in cyclical references.

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.

The point is to place the #JsonIgnore in the setter method as follow. in my case.
Township.java
#Access(AccessType.PROPERTY)
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="townshipId", nullable=false ,insertable=false, updatable=false)
public List<Village> getVillages() {
return villages;
}
#JsonIgnore
#Access(AccessType.PROPERTY)
public void setVillages(List<Village> villages) {
this.villages = villages;
}
Village.java
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "townshipId", insertable=false, updatable=false)
Township township;
#Column(name = "townshipId", nullable=false)
Long townshipId;

I have faced same issue, add jsonbackref and jsonmanagedref and please make sure #override equals and hashCode methods , this definitely fix this issue.

Related

Kotlin Springboot Many-to-Many Entity class cause recursive data [duplicate]

When trying to convert a JPA object that has a bi-directional association into JSON, I keep getting
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
All I found is this thread which basically concludes with recommending to avoid bi-directional associations. Does anyone have an idea for a workaround for this spring bug?
------ EDIT 2010-07-24 16:26:22 -------
Codesnippets:
Business Object 1:
#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)
private Set<BodyStat> bodyStats;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<Training> trainings;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<ExerciseType> exerciseTypes;
public Trainee() {
super();
}
//... getters/setters ...
}
Business Object 2:
import javax.persistence.*;
import java.util.Date;
#Entity
#Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "height", nullable = true)
private Float height;
#Column(name = "measuretime", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date measureTime;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="trainee_fk")
private Trainee trainee;
}
Controller:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
#Controller
#RequestMapping(value = "/trainees")
public class TraineesController {
final Logger logger = LoggerFactory.getLogger(TraineesController.class);
private Map<Long, Trainee> trainees = new ConcurrentHashMap<Long, Trainee>();
#Autowired
private ITraineeDAO traineeDAO;
/**
* Return json repres. of all trainees
*/
#RequestMapping(value = "/getAllTrainees", method = RequestMethod.GET)
#ResponseBody
public Collection getAllTrainees() {
Collection allTrainees = this.traineeDAO.getAll();
this.logger.debug("A total of " + allTrainees.size() + " trainees was read from db");
return allTrainees;
}
}
JPA-implementation of the trainee DAO:
#Repository
#Transactional
public class TraineeDAO implements ITraineeDAO {
#PersistenceContext
private EntityManager em;
#Transactional
public Trainee save(Trainee trainee) {
em.persist(trainee);
return trainee;
}
#Transactional(readOnly = true)
public Collection getAll() {
return (Collection) em.createQuery("SELECT t FROM Trainee t").getResultList();
}
}
persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="RDBMS" transaction-type="RESOURCE_LOCAL">
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.hbm2ddl.auto" value="validate"/>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<!-- <property name="dialect" value="org.hibernate.dialect.HSQLDialect"/> -->
</properties>
</persistence-unit>
</persistence>
JsonIgnoreProperties [2017 Update]:
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).
JsonManagedReference and JsonBackReference
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:
Keenformatics - How To Solve JSON infinite recursion Stackoverflow (my blog)
Jackson References
Personal experience
You may use #JsonIgnore to break the cycle (reference).
You need to import org.codehaus.jackson.annotate.JsonIgnore (legacy versions) or com.fasterxml.jackson.annotation.JsonIgnore (current versions).
The new annotation #JsonIgnoreProperties resolves many of the issues with the other options.
#Entity
public class Material{
...
#JsonIgnoreProperties("costMaterials")
private List<Supplier> costSuppliers = new ArrayList<>();
...
}
#Entity
public class Supplier{
...
#JsonIgnoreProperties("costSuppliers")
private List<Material> costMaterials = new ArrayList<>();
....
}
Check it out here. It works just like in the documentation:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html
Also, using Jackson 2.0+ you can use #JsonIdentityInfo. This worked much better for my hibernate classes than #JsonBackReference and #JsonManagedReference, which had problems for me and did not solve the issue. Just add something like:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#traineeId")
public class Trainee extends BusinessObject {
#Entity
#Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#bodyStatId")
public class BodyStat extends BusinessObject {
and it should work.
Also, Jackson 1.6 has support for handling bi-directional references... which seems like
what you are looking for (this blog entry also mentions the feature)
And as of July 2011, there is also "jackson-module-hibernate" which might help in some aspects of dealing with Hibernate objects, although not necessarily this particular one (which does require annotations).
This worked perfectly fine for me.
Add the annotation #JsonIgnore on the child class where you mention the reference to the parent class.
#ManyToOne
#JoinColumn(name = "ID", nullable = false, updatable = false)
#JsonIgnore
private Member member;
Now Jackson supports avoiding cycles without ignoring the fields:
Jackson - serialization of entities with birectional relationships (avoiding cycles)
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;
For me the best solution is to use #JsonView and create specific filters for each scenario. You could also use #JsonManagedReference and #JsonBackReference, however it is a hardcoded solution to only one situation, where the owner always references the owning side, and never the opposite. If you have another serialization scenario where you need to re-annotate the attribute differently, you will not be able to.
Problem
Lets use two classes, Company and Employee where you have a cyclic dependency between them:
public class Company {
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
public class Employee {
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
And the test class that tries to serialize using ObjectMapper (Spring Boot):
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
public class CompanyTest {
#Autowired
public ObjectMapper mapper;
#Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
String jsonCompany = mapper.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
If you run this code, you'll get the:
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
Solution Using `#JsonView`
#JsonView enables you to use filters and choose what fields should be included while serializing the objects. A filter is just a class reference used as a identifier. So let's first create the filters:
public class Filter {
public static interface EmployeeData {};
public static interface CompanyData extends EmployeeData {};
}
Remember, the filters are dummy classes, just used for specifying the fields with the #JsonView annotation, so you can create as many as you want and need. Let's see it in action, but first we need to annotate our Company class:
public class Company {
#JsonView(Filter.CompanyData.class)
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
and change the Test in order for the serializer to use the View:
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
public class CompanyTest {
#Autowired
public ObjectMapper mapper;
#Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
ObjectWriter writter = mapper.writerWithView(Filter.CompanyData.class);
String jsonCompany = writter.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
Now if you run this code, the Infinite Recursion problem is solved, because you have explicitly said that you just want to serialize the attributes that were annotated with #JsonView(Filter.CompanyData.class).
When it reaches the back reference for company in the Employee, it checks that it's not annotated and ignore the serialization. You also have a powerful and flexible solution to choose which data you want to send through your REST APIs.
With Spring you can annotate your REST Controllers methods with the desired #JsonView filter and the serialization is applied transparently to the returning object.
Here are the imports used in case you need to check:
import static org.junit.Assert.assertTrue;
import javax.transaction.Transactional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.annotation.JsonView;
There's now a Jackson module (for Jackson 2) specifically designed to handle Hibernate lazy initialization problems when serializing.
https://github.com/FasterXML/jackson-datatype-hibernate
Just add the dependency (note there are different dependencies for Hibernate 3 and Hibernate 4):
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.4.0</version>
</dependency>
and then register the module when intializing Jackson's ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate4Module());
Documentation currently isn't great. See the Hibernate4Module code for available options.
#JsonIgnoreProperties is the answer.
Use something like this ::
#OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
#JsonIgnoreProperties("course")
private Set<Student> students;
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 my case it was enough to change relation from:
#OneToMany(mappedBy = "county")
private List<Town> towns;
to:
#OneToMany
private List<Town> towns;
another relation stayed as it was:
#ManyToOne
#JoinColumn(name = "county_id")
private County county;
I also met the same problem. I used #JsonIdentityInfo's ObjectIdGenerators.PropertyGenerator.class generator type.
That's my solution:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Trainee extends BusinessObject {
...
VERY IMPORTANT: If you are using LOMBOK, make shure to exclude attributes of collections like Set, List, etc...
Like this:
#EqualsAndHashCode(exclude = {"attributeOfTypeList", "attributeOfTypeSet"})
Be sure you use com.fasterxml.jackson everywhere. I spent much time to find it out.
<properties>
<fasterxml.jackson.version>2.9.2</fasterxml.jackson.version>
</properties>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${fasterxml.jackson.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${fasterxml.jackson.version}</version>
</dependency>
Then use #JsonManagedReference and #JsonBackReference.
Finally, you can serialize your model to JSON:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(model);
You can use #JsonIgnore, but this will ignore the json data which can be accessed because of the Foreign Key relationship. Therefore if you reqiure the foreign key data (most of the time we require), then #JsonIgnore will not help you. In such situation please follow the below solution.
you are getting Infinite recursion, because of the BodyStat class again referring the Trainee object
BodyStat
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="trainee_fk")
private Trainee trainee;
Trainee
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<BodyStat> bodyStats;
Therefore, you have to comment/omit the above part in Trainee
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;
you can use DTO pattern
create class TraineeDTO without any anotation hiberbnate and you can use jackson mapper to convert Trainee to TraineeDTO and bingo the error message disapeare :)
If you cannot ignore the property, try modifying the visibility of the field. In our case, we had old code still submitting entities with the relationship, so in my case, this was the fix:
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Trainee trainee;
For some reason, in my case, it wasn't working with Set. I had to change it to List and use #JsonIgnore and #ToString.Exclude to get it working.
Replace Set with List:
//before
#OneToMany(mappedBy="client")
private Set<address> addressess;
//after
#OneToMany(mappedBy="client")
private List<address> addressess;
And add #JsonIgnore and #ToString.Exclude annotations:
#ManyToOne
#JoinColumn(name="client_id", nullable = false)
#JsonIgnore
#ToString.Exclude
private Client client;
If you use #JsonManagedReference, #JsonBackReference or #JsonIgnore annotation it ignore some fields and solve Infinite Recursion with Jackson JSON.
But if you use #JsonIdentityInfo which also avoid the Infinite Recursion and you can get all the fields values, so I suggest that you use #JsonIdentityInfo annotation.
#JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator.class, property="#id")
Refer this article https://www.toptal.com/javascript/bidirectional-relationship-in-json to get good understanding about #JsonIdentityInfo annotation.
This post: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
has a full explanation.
If you are using Jackson with older versions, you can try #jsonmanagedreference + #jsonbackreference. If your Jackson is above 2 (1.9 also doesn't work as I know), try #JsonIdentityInfo instead.
As someone using Spring Data and Lombok, this is how I solved it for myself.
#Entity
#Data
public class Foo extends BaseEntity {
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "foo_id")
#JsonIgnoreProperties("parent_foo")
#EqualsAndHashCode.Exclude
private Set<Bar> linkedBars;
}
#Entity
#Data
public class Bar extends BaseEntity {
#Column(name = "foo_id")
private Long parentFooId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "foo_id", insertable = false, updatable = false)
#JsonIgnoreProperties({"linkedBars"})
private Foo parentFoo;
}
The JsonIgnoreProperties annotation stops infinite recursion as many answers have discussed above.
#EqualsAndHashCode.Exclude prevents the StackOverflowError caused by hashCode and equals being called recursively.
Using Set over List resolves the MultipleBagFetchException which occurs when you add multiple collection fields. You can also use #Fetch(value = FetchMode.SUBSELECT) to avoid the cartesian product, but I haven't tried it personally since my use case didn't need it.
The explicit definition of parentFooId in Bar is to allow mapping Foo entities with Bars.
I had this problem, but I didn't want to use annotation in my entities, so I solved by creating a constructor for my class, this constructor must not have a reference back to the entities who references this entity. Let's say this scenario.
public class A{
private int id;
private String code;
private String name;
private List<B> bs;
}
public class B{
private int id;
private String code;
private String name;
private A a;
}
If you try to send to the view the class B or A with #ResponseBody it may cause an infinite loop. You can write a constructor in your class and create a query with your entityManager like this.
"select new A(id, code, name) from A"
This is the class with the constructor.
public class A{
private int id;
private String code;
private String name;
private List<B> bs;
public A(){
}
public A(int id, String code, String name){
this.id = id;
this.code = code;
this.name = name;
}
}
However, there are some constrictions about this solution, as you can see, in the constructor I did not make a reference to List bs this is because Hibernate does not allow it, at least in version 3.6.10.Final, so when I need to show both entities in a view I do the following.
public A getAById(int id); //THE A id
public List<B> getBsByAId(int idA); //the A id.
The other problem with this solution, is that if you add or remove a property you must update your constructor and all your queries.
In case you are using Spring Data Rest, issue can be resolved by creating Repositories for every Entity involved in cyclical references.
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.
The point is to place the #JsonIgnore in the setter method as follow. in my case.
Township.java
#Access(AccessType.PROPERTY)
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="townshipId", nullable=false ,insertable=false, updatable=false)
public List<Village> getVillages() {
return villages;
}
#JsonIgnore
#Access(AccessType.PROPERTY)
public void setVillages(List<Village> villages) {
this.villages = villages;
}
Village.java
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "townshipId", insertable=false, updatable=false)
Township township;
#Column(name = "townshipId", nullable=false)
Long townshipId;
I have faced same issue, add jsonbackref and jsonmanagedref and please make sure #override equals and hashCode methods , this definitely fix this issue.

Why when i use #Query with nativeQuery execute the JPA Query too [duplicate]

When trying to convert a JPA object that has a bi-directional association into JSON, I keep getting
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
All I found is this thread which basically concludes with recommending to avoid bi-directional associations. Does anyone have an idea for a workaround for this spring bug?
------ EDIT 2010-07-24 16:26:22 -------
Codesnippets:
Business Object 1:
#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)
private Set<BodyStat> bodyStats;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<Training> trainings;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<ExerciseType> exerciseTypes;
public Trainee() {
super();
}
//... getters/setters ...
}
Business Object 2:
import javax.persistence.*;
import java.util.Date;
#Entity
#Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "height", nullable = true)
private Float height;
#Column(name = "measuretime", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date measureTime;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="trainee_fk")
private Trainee trainee;
}
Controller:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
#Controller
#RequestMapping(value = "/trainees")
public class TraineesController {
final Logger logger = LoggerFactory.getLogger(TraineesController.class);
private Map<Long, Trainee> trainees = new ConcurrentHashMap<Long, Trainee>();
#Autowired
private ITraineeDAO traineeDAO;
/**
* Return json repres. of all trainees
*/
#RequestMapping(value = "/getAllTrainees", method = RequestMethod.GET)
#ResponseBody
public Collection getAllTrainees() {
Collection allTrainees = this.traineeDAO.getAll();
this.logger.debug("A total of " + allTrainees.size() + " trainees was read from db");
return allTrainees;
}
}
JPA-implementation of the trainee DAO:
#Repository
#Transactional
public class TraineeDAO implements ITraineeDAO {
#PersistenceContext
private EntityManager em;
#Transactional
public Trainee save(Trainee trainee) {
em.persist(trainee);
return trainee;
}
#Transactional(readOnly = true)
public Collection getAll() {
return (Collection) em.createQuery("SELECT t FROM Trainee t").getResultList();
}
}
persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="RDBMS" transaction-type="RESOURCE_LOCAL">
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.hbm2ddl.auto" value="validate"/>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<!-- <property name="dialect" value="org.hibernate.dialect.HSQLDialect"/> -->
</properties>
</persistence-unit>
</persistence>
JsonIgnoreProperties [2017 Update]:
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).
JsonManagedReference and JsonBackReference
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:
Keenformatics - How To Solve JSON infinite recursion Stackoverflow (my blog)
Jackson References
Personal experience
You may use #JsonIgnore to break the cycle (reference).
You need to import org.codehaus.jackson.annotate.JsonIgnore (legacy versions) or com.fasterxml.jackson.annotation.JsonIgnore (current versions).
The new annotation #JsonIgnoreProperties resolves many of the issues with the other options.
#Entity
public class Material{
...
#JsonIgnoreProperties("costMaterials")
private List<Supplier> costSuppliers = new ArrayList<>();
...
}
#Entity
public class Supplier{
...
#JsonIgnoreProperties("costSuppliers")
private List<Material> costMaterials = new ArrayList<>();
....
}
Check it out here. It works just like in the documentation:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html
Also, using Jackson 2.0+ you can use #JsonIdentityInfo. This worked much better for my hibernate classes than #JsonBackReference and #JsonManagedReference, which had problems for me and did not solve the issue. Just add something like:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#traineeId")
public class Trainee extends BusinessObject {
#Entity
#Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#bodyStatId")
public class BodyStat extends BusinessObject {
and it should work.
Also, Jackson 1.6 has support for handling bi-directional references... which seems like
what you are looking for (this blog entry also mentions the feature)
And as of July 2011, there is also "jackson-module-hibernate" which might help in some aspects of dealing with Hibernate objects, although not necessarily this particular one (which does require annotations).
This worked perfectly fine for me.
Add the annotation #JsonIgnore on the child class where you mention the reference to the parent class.
#ManyToOne
#JoinColumn(name = "ID", nullable = false, updatable = false)
#JsonIgnore
private Member member;
Now Jackson supports avoiding cycles without ignoring the fields:
Jackson - serialization of entities with birectional relationships (avoiding cycles)
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;
For me the best solution is to use #JsonView and create specific filters for each scenario. You could also use #JsonManagedReference and #JsonBackReference, however it is a hardcoded solution to only one situation, where the owner always references the owning side, and never the opposite. If you have another serialization scenario where you need to re-annotate the attribute differently, you will not be able to.
Problem
Lets use two classes, Company and Employee where you have a cyclic dependency between them:
public class Company {
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
public class Employee {
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
And the test class that tries to serialize using ObjectMapper (Spring Boot):
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
public class CompanyTest {
#Autowired
public ObjectMapper mapper;
#Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
String jsonCompany = mapper.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
If you run this code, you'll get the:
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
Solution Using `#JsonView`
#JsonView enables you to use filters and choose what fields should be included while serializing the objects. A filter is just a class reference used as a identifier. So let's first create the filters:
public class Filter {
public static interface EmployeeData {};
public static interface CompanyData extends EmployeeData {};
}
Remember, the filters are dummy classes, just used for specifying the fields with the #JsonView annotation, so you can create as many as you want and need. Let's see it in action, but first we need to annotate our Company class:
public class Company {
#JsonView(Filter.CompanyData.class)
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
and change the Test in order for the serializer to use the View:
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
public class CompanyTest {
#Autowired
public ObjectMapper mapper;
#Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
ObjectWriter writter = mapper.writerWithView(Filter.CompanyData.class);
String jsonCompany = writter.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
Now if you run this code, the Infinite Recursion problem is solved, because you have explicitly said that you just want to serialize the attributes that were annotated with #JsonView(Filter.CompanyData.class).
When it reaches the back reference for company in the Employee, it checks that it's not annotated and ignore the serialization. You also have a powerful and flexible solution to choose which data you want to send through your REST APIs.
With Spring you can annotate your REST Controllers methods with the desired #JsonView filter and the serialization is applied transparently to the returning object.
Here are the imports used in case you need to check:
import static org.junit.Assert.assertTrue;
import javax.transaction.Transactional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.annotation.JsonView;
There's now a Jackson module (for Jackson 2) specifically designed to handle Hibernate lazy initialization problems when serializing.
https://github.com/FasterXML/jackson-datatype-hibernate
Just add the dependency (note there are different dependencies for Hibernate 3 and Hibernate 4):
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.4.0</version>
</dependency>
and then register the module when intializing Jackson's ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate4Module());
Documentation currently isn't great. See the Hibernate4Module code for available options.
#JsonIgnoreProperties is the answer.
Use something like this ::
#OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
#JsonIgnoreProperties("course")
private Set<Student> students;
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 my case it was enough to change relation from:
#OneToMany(mappedBy = "county")
private List<Town> towns;
to:
#OneToMany
private List<Town> towns;
another relation stayed as it was:
#ManyToOne
#JoinColumn(name = "county_id")
private County county;
I also met the same problem. I used #JsonIdentityInfo's ObjectIdGenerators.PropertyGenerator.class generator type.
That's my solution:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Trainee extends BusinessObject {
...
VERY IMPORTANT: If you are using LOMBOK, make shure to exclude attributes of collections like Set, List, etc...
Like this:
#EqualsAndHashCode(exclude = {"attributeOfTypeList", "attributeOfTypeSet"})
Be sure you use com.fasterxml.jackson everywhere. I spent much time to find it out.
<properties>
<fasterxml.jackson.version>2.9.2</fasterxml.jackson.version>
</properties>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${fasterxml.jackson.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${fasterxml.jackson.version}</version>
</dependency>
Then use #JsonManagedReference and #JsonBackReference.
Finally, you can serialize your model to JSON:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(model);
You can use #JsonIgnore, but this will ignore the json data which can be accessed because of the Foreign Key relationship. Therefore if you reqiure the foreign key data (most of the time we require), then #JsonIgnore will not help you. In such situation please follow the below solution.
you are getting Infinite recursion, because of the BodyStat class again referring the Trainee object
BodyStat
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="trainee_fk")
private Trainee trainee;
Trainee
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<BodyStat> bodyStats;
Therefore, you have to comment/omit the above part in Trainee
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;
you can use DTO pattern
create class TraineeDTO without any anotation hiberbnate and you can use jackson mapper to convert Trainee to TraineeDTO and bingo the error message disapeare :)
If you cannot ignore the property, try modifying the visibility of the field. In our case, we had old code still submitting entities with the relationship, so in my case, this was the fix:
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Trainee trainee;
For some reason, in my case, it wasn't working with Set. I had to change it to List and use #JsonIgnore and #ToString.Exclude to get it working.
Replace Set with List:
//before
#OneToMany(mappedBy="client")
private Set<address> addressess;
//after
#OneToMany(mappedBy="client")
private List<address> addressess;
And add #JsonIgnore and #ToString.Exclude annotations:
#ManyToOne
#JoinColumn(name="client_id", nullable = false)
#JsonIgnore
#ToString.Exclude
private Client client;
If you use #JsonManagedReference, #JsonBackReference or #JsonIgnore annotation it ignore some fields and solve Infinite Recursion with Jackson JSON.
But if you use #JsonIdentityInfo which also avoid the Infinite Recursion and you can get all the fields values, so I suggest that you use #JsonIdentityInfo annotation.
#JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator.class, property="#id")
Refer this article https://www.toptal.com/javascript/bidirectional-relationship-in-json to get good understanding about #JsonIdentityInfo annotation.
This post: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
has a full explanation.
If you are using Jackson with older versions, you can try #jsonmanagedreference + #jsonbackreference. If your Jackson is above 2 (1.9 also doesn't work as I know), try #JsonIdentityInfo instead.
As someone using Spring Data and Lombok, this is how I solved it for myself.
#Entity
#Data
public class Foo extends BaseEntity {
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "foo_id")
#JsonIgnoreProperties("parent_foo")
#EqualsAndHashCode.Exclude
private Set<Bar> linkedBars;
}
#Entity
#Data
public class Bar extends BaseEntity {
#Column(name = "foo_id")
private Long parentFooId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "foo_id", insertable = false, updatable = false)
#JsonIgnoreProperties({"linkedBars"})
private Foo parentFoo;
}
The JsonIgnoreProperties annotation stops infinite recursion as many answers have discussed above.
#EqualsAndHashCode.Exclude prevents the StackOverflowError caused by hashCode and equals being called recursively.
Using Set over List resolves the MultipleBagFetchException which occurs when you add multiple collection fields. You can also use #Fetch(value = FetchMode.SUBSELECT) to avoid the cartesian product, but I haven't tried it personally since my use case didn't need it.
The explicit definition of parentFooId in Bar is to allow mapping Foo entities with Bars.
I had this problem, but I didn't want to use annotation in my entities, so I solved by creating a constructor for my class, this constructor must not have a reference back to the entities who references this entity. Let's say this scenario.
public class A{
private int id;
private String code;
private String name;
private List<B> bs;
}
public class B{
private int id;
private String code;
private String name;
private A a;
}
If you try to send to the view the class B or A with #ResponseBody it may cause an infinite loop. You can write a constructor in your class and create a query with your entityManager like this.
"select new A(id, code, name) from A"
This is the class with the constructor.
public class A{
private int id;
private String code;
private String name;
private List<B> bs;
public A(){
}
public A(int id, String code, String name){
this.id = id;
this.code = code;
this.name = name;
}
}
However, there are some constrictions about this solution, as you can see, in the constructor I did not make a reference to List bs this is because Hibernate does not allow it, at least in version 3.6.10.Final, so when I need to show both entities in a view I do the following.
public A getAById(int id); //THE A id
public List<B> getBsByAId(int idA); //the A id.
The other problem with this solution, is that if you add or remove a property you must update your constructor and all your queries.
In case you are using Spring Data Rest, issue can be resolved by creating Repositories for every Entity involved in cyclical references.
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.
The point is to place the #JsonIgnore in the setter method as follow. in my case.
Township.java
#Access(AccessType.PROPERTY)
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="townshipId", nullable=false ,insertable=false, updatable=false)
public List<Village> getVillages() {
return villages;
}
#JsonIgnore
#Access(AccessType.PROPERTY)
public void setVillages(List<Village> villages) {
this.villages = villages;
}
Village.java
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "townshipId", insertable=false, updatable=false)
Township township;
#Column(name = "townshipId", nullable=false)
Long townshipId;
I have faced same issue, add jsonbackref and jsonmanagedref and please make sure #override equals and hashCode methods , this definitely fix this issue.

Infinite recursion with Jackson on intermediate table

I am using Spring Boot and Jackson and Hibernate to create an API. Hibernate connects to a MySQL database.
I understand the good practices but I'm stuck on a particular point.
I have an n:m relationship that contains an extra field.
Ex: Author(id, ...) -> Written(idAuthor, idBook, date) <- Book(id, ...)
I understand how to map a traditional n:m relationship, but this technique does not apply to me this time.
For this, I found a source on the internet that showed the solution: create an intermediate class in my code that contains an Author type object and a Book type object + my additional fields.
#Entity
#Table(name = "Author")
public class Author implements Serializable {
/...
#Id
#GeneratedValue
private int id;
#OneToMany(mappedBy = "author", cascade = CascadeType.ALL)
private Set<Written> written= new HashSet<>();
/...
}
#Entity
#Table(name = "Book")
public class Book implements Serializable{
/...
#Id
#GeneratedValue
private int id;
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL)
private Set<Written> written= new HashSet<>();
/...
}
public class Written implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "idAuthor")
private Author author;
#Id
#ManyToOne
#JoinColumn(name = "idBook")
private Book book;
//Extra fields ....
}
That's a bidirectional link.
With this code, I get an infinite recursivity error:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]->com.exampleAPI.api.model.Book["written"])]
I tried to use #JsonIgnore, #JsonManagedReference and #JsonBackReference on the Written class, also tried to use transient keyword, but nothing worked.
I can't find any source on the internet that could help me, and neither can the documentation for this particular case.
Can someone help me?
When unhandled bidirectional relationship occurs, Jackson faces infinite recursion.
I tried to use #JsonIgnore, #JsonManagedReference and #JsonBackReference on the Written class
You need to use #JsonManagedReference and #JsonBackReference annotations separately to prevent these cycles between Book and Written. A side note, transient has nothing to do with the persistence but the serialization. JPA works with the #Transient annotation.
public class Book implements Serializable {
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL)
#JsonBackReference
private Set<Written> written= new HashSet<>();
...
}
public class Written implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "idBook")
#JsonManagedReference
private Book book;
...
}
Important: Don't send database entities through REST (probably what you are up to do). Better create a DAO object without bidirectional relationship and map entities into DAOs. There are several libraries able to do that: I highly recommend MapStruct, however ModelMapper is also an option. If there is a lower number of such entities, using constructors/getters/setters would be enough.

Hibernate initializes collection with immutable list

I have the following entity:
#Entity(name = "courses")
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Singular
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Collection<Comment> comments = new ArrayList<>();
#ManyToOne(fetch = FetchType.LAZY, optional = false)
private User createdBy;
public void addComment(Comment comment) {
comments.add(comment);
}
}
When I load a course from the database, comments are initialized with a Persistent Bag. The problem occurs when I call addComment(comment), I then get this exception:
Caused by: java.lang.UnsupportedOperationException: null
at java.base/java.util.AbstractList.add(AbstractList.java:153) ~[na:na]
at java.base/java.util.AbstractList.add(AbstractList.java:111) ~[na:na]
at org.hibernate.collection.internal.PersistentBag.add(PersistentBag.java:408) ~[hibernate-core-5.4.18.Final.jar:5.4.18.Final]
It seems that the Persistant Bag, which hibernates assigns, is a Collection.EmptyList, which is immutable and, as such, no comments can be added.
What do I need to change to be able to add comments?
Ok so it seems like lombok and hibernate where not really working together as I expected. In addition I had my entity annotated with the following lombok notations:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Course {
//...
#Singular
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
}
When I loaded an entity from the DB it seems lombok uses the builder pattern to create a new instance. This will result in comment having a Collection.EmptyList assigned and then no new comments can be added since it is immutable. My fix was/is to remove the #Singular and add a #Builder.Default. Now a new ArrayList<>() is assigned instead the immutable list. Now I can't use the created singular builder methods but at least it works.

Jackson - couldn't write json - infinite recurison [duplicate]

When trying to convert a JPA object that has a bi-directional association into JSON, I keep getting
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
All I found is this thread which basically concludes with recommending to avoid bi-directional associations. Does anyone have an idea for a workaround for this spring bug?
------ EDIT 2010-07-24 16:26:22 -------
Codesnippets:
Business Object 1:
#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)
private Set<BodyStat> bodyStats;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<Training> trainings;
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<ExerciseType> exerciseTypes;
public Trainee() {
super();
}
//... getters/setters ...
}
Business Object 2:
import javax.persistence.*;
import java.util.Date;
#Entity
#Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "height", nullable = true)
private Float height;
#Column(name = "measuretime", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date measureTime;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="trainee_fk")
private Trainee trainee;
}
Controller:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
#Controller
#RequestMapping(value = "/trainees")
public class TraineesController {
final Logger logger = LoggerFactory.getLogger(TraineesController.class);
private Map<Long, Trainee> trainees = new ConcurrentHashMap<Long, Trainee>();
#Autowired
private ITraineeDAO traineeDAO;
/**
* Return json repres. of all trainees
*/
#RequestMapping(value = "/getAllTrainees", method = RequestMethod.GET)
#ResponseBody
public Collection getAllTrainees() {
Collection allTrainees = this.traineeDAO.getAll();
this.logger.debug("A total of " + allTrainees.size() + " trainees was read from db");
return allTrainees;
}
}
JPA-implementation of the trainee DAO:
#Repository
#Transactional
public class TraineeDAO implements ITraineeDAO {
#PersistenceContext
private EntityManager em;
#Transactional
public Trainee save(Trainee trainee) {
em.persist(trainee);
return trainee;
}
#Transactional(readOnly = true)
public Collection getAll() {
return (Collection) em.createQuery("SELECT t FROM Trainee t").getResultList();
}
}
persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="RDBMS" transaction-type="RESOURCE_LOCAL">
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.hbm2ddl.auto" value="validate"/>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<!-- <property name="dialect" value="org.hibernate.dialect.HSQLDialect"/> -->
</properties>
</persistence-unit>
</persistence>
JsonIgnoreProperties [2017 Update]:
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).
JsonManagedReference and JsonBackReference
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:
Keenformatics - How To Solve JSON infinite recursion Stackoverflow (my blog)
Jackson References
Personal experience
You may use #JsonIgnore to break the cycle (reference).
You need to import org.codehaus.jackson.annotate.JsonIgnore (legacy versions) or com.fasterxml.jackson.annotation.JsonIgnore (current versions).
The new annotation #JsonIgnoreProperties resolves many of the issues with the other options.
#Entity
public class Material{
...
#JsonIgnoreProperties("costMaterials")
private List<Supplier> costSuppliers = new ArrayList<>();
...
}
#Entity
public class Supplier{
...
#JsonIgnoreProperties("costSuppliers")
private List<Material> costMaterials = new ArrayList<>();
....
}
Check it out here. It works just like in the documentation:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html
Also, using Jackson 2.0+ you can use #JsonIdentityInfo. This worked much better for my hibernate classes than #JsonBackReference and #JsonManagedReference, which had problems for me and did not solve the issue. Just add something like:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#traineeId")
public class Trainee extends BusinessObject {
#Entity
#Table(name = "ta_bodystat", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#bodyStatId")
public class BodyStat extends BusinessObject {
and it should work.
Also, Jackson 1.6 has support for handling bi-directional references... which seems like
what you are looking for (this blog entry also mentions the feature)
And as of July 2011, there is also "jackson-module-hibernate" which might help in some aspects of dealing with Hibernate objects, although not necessarily this particular one (which does require annotations).
This worked perfectly fine for me.
Add the annotation #JsonIgnore on the child class where you mention the reference to the parent class.
#ManyToOne
#JoinColumn(name = "ID", nullable = false, updatable = false)
#JsonIgnore
private Member member;
Now Jackson supports avoiding cycles without ignoring the fields:
Jackson - serialization of entities with birectional relationships (avoiding cycles)
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;
For me the best solution is to use #JsonView and create specific filters for each scenario. You could also use #JsonManagedReference and #JsonBackReference, however it is a hardcoded solution to only one situation, where the owner always references the owning side, and never the opposite. If you have another serialization scenario where you need to re-annotate the attribute differently, you will not be able to.
Problem
Lets use two classes, Company and Employee where you have a cyclic dependency between them:
public class Company {
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
public class Employee {
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
And the test class that tries to serialize using ObjectMapper (Spring Boot):
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
public class CompanyTest {
#Autowired
public ObjectMapper mapper;
#Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
String jsonCompany = mapper.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
If you run this code, you'll get the:
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
Solution Using `#JsonView`
#JsonView enables you to use filters and choose what fields should be included while serializing the objects. A filter is just a class reference used as a identifier. So let's first create the filters:
public class Filter {
public static interface EmployeeData {};
public static interface CompanyData extends EmployeeData {};
}
Remember, the filters are dummy classes, just used for specifying the fields with the #JsonView annotation, so you can create as many as you want and need. Let's see it in action, but first we need to annotate our Company class:
public class Company {
#JsonView(Filter.CompanyData.class)
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
and change the Test in order for the serializer to use the View:
#SpringBootTest
#RunWith(SpringRunner.class)
#Transactional
public class CompanyTest {
#Autowired
public ObjectMapper mapper;
#Test
public void shouldSaveCompany() throws JsonProcessingException {
Employee employee = new Employee();
Company company = new Company(employee);
employee.setCompany(company);
ObjectWriter writter = mapper.writerWithView(Filter.CompanyData.class);
String jsonCompany = writter.writeValueAsString(company);
System.out.println(jsonCompany);
assertTrue(true);
}
}
Now if you run this code, the Infinite Recursion problem is solved, because you have explicitly said that you just want to serialize the attributes that were annotated with #JsonView(Filter.CompanyData.class).
When it reaches the back reference for company in the Employee, it checks that it's not annotated and ignore the serialization. You also have a powerful and flexible solution to choose which data you want to send through your REST APIs.
With Spring you can annotate your REST Controllers methods with the desired #JsonView filter and the serialization is applied transparently to the returning object.
Here are the imports used in case you need to check:
import static org.junit.Assert.assertTrue;
import javax.transaction.Transactional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.annotation.JsonView;
There's now a Jackson module (for Jackson 2) specifically designed to handle Hibernate lazy initialization problems when serializing.
https://github.com/FasterXML/jackson-datatype-hibernate
Just add the dependency (note there are different dependencies for Hibernate 3 and Hibernate 4):
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.4.0</version>
</dependency>
and then register the module when intializing Jackson's ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate4Module());
Documentation currently isn't great. See the Hibernate4Module code for available options.
#JsonIgnoreProperties is the answer.
Use something like this ::
#OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
#JsonIgnoreProperties("course")
private Set<Student> students;
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 my case it was enough to change relation from:
#OneToMany(mappedBy = "county")
private List<Town> towns;
to:
#OneToMany
private List<Town> towns;
another relation stayed as it was:
#ManyToOne
#JoinColumn(name = "county_id")
private County county;
I also met the same problem. I used #JsonIdentityInfo's ObjectIdGenerators.PropertyGenerator.class generator type.
That's my solution:
#Entity
#Table(name = "ta_trainee", uniqueConstraints = {#UniqueConstraint(columnNames = {"id"})})
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Trainee extends BusinessObject {
...
VERY IMPORTANT: If you are using LOMBOK, make shure to exclude attributes of collections like Set, List, etc...
Like this:
#EqualsAndHashCode(exclude = {"attributeOfTypeList", "attributeOfTypeSet"})
Be sure you use com.fasterxml.jackson everywhere. I spent much time to find it out.
<properties>
<fasterxml.jackson.version>2.9.2</fasterxml.jackson.version>
</properties>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${fasterxml.jackson.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${fasterxml.jackson.version}</version>
</dependency>
Then use #JsonManagedReference and #JsonBackReference.
Finally, you can serialize your model to JSON:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(model);
You can use #JsonIgnore, but this will ignore the json data which can be accessed because of the Foreign Key relationship. Therefore if you reqiure the foreign key data (most of the time we require), then #JsonIgnore will not help you. In such situation please follow the below solution.
you are getting Infinite recursion, because of the BodyStat class again referring the Trainee object
BodyStat
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name="trainee_fk")
private Trainee trainee;
Trainee
#OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Column(nullable = true)
private Set<BodyStat> bodyStats;
Therefore, you have to comment/omit the above part in Trainee
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;
you can use DTO pattern
create class TraineeDTO without any anotation hiberbnate and you can use jackson mapper to convert Trainee to TraineeDTO and bingo the error message disapeare :)
If you cannot ignore the property, try modifying the visibility of the field. In our case, we had old code still submitting entities with the relationship, so in my case, this was the fix:
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Trainee trainee;
For some reason, in my case, it wasn't working with Set. I had to change it to List and use #JsonIgnore and #ToString.Exclude to get it working.
Replace Set with List:
//before
#OneToMany(mappedBy="client")
private Set<address> addressess;
//after
#OneToMany(mappedBy="client")
private List<address> addressess;
And add #JsonIgnore and #ToString.Exclude annotations:
#ManyToOne
#JoinColumn(name="client_id", nullable = false)
#JsonIgnore
#ToString.Exclude
private Client client;
If you use #JsonManagedReference, #JsonBackReference or #JsonIgnore annotation it ignore some fields and solve Infinite Recursion with Jackson JSON.
But if you use #JsonIdentityInfo which also avoid the Infinite Recursion and you can get all the fields values, so I suggest that you use #JsonIdentityInfo annotation.
#JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator.class, property="#id")
Refer this article https://www.toptal.com/javascript/bidirectional-relationship-in-json to get good understanding about #JsonIdentityInfo annotation.
This post: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion
has a full explanation.
If you are using Jackson with older versions, you can try #jsonmanagedreference + #jsonbackreference. If your Jackson is above 2 (1.9 also doesn't work as I know), try #JsonIdentityInfo instead.
As someone using Spring Data and Lombok, this is how I solved it for myself.
#Entity
#Data
public class Foo extends BaseEntity {
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "foo_id")
#JsonIgnoreProperties("parent_foo")
#EqualsAndHashCode.Exclude
private Set<Bar> linkedBars;
}
#Entity
#Data
public class Bar extends BaseEntity {
#Column(name = "foo_id")
private Long parentFooId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "foo_id", insertable = false, updatable = false)
#JsonIgnoreProperties({"linkedBars"})
private Foo parentFoo;
}
The JsonIgnoreProperties annotation stops infinite recursion as many answers have discussed above.
#EqualsAndHashCode.Exclude prevents the StackOverflowError caused by hashCode and equals being called recursively.
Using Set over List resolves the MultipleBagFetchException which occurs when you add multiple collection fields. You can also use #Fetch(value = FetchMode.SUBSELECT) to avoid the cartesian product, but I haven't tried it personally since my use case didn't need it.
The explicit definition of parentFooId in Bar is to allow mapping Foo entities with Bars.
I had this problem, but I didn't want to use annotation in my entities, so I solved by creating a constructor for my class, this constructor must not have a reference back to the entities who references this entity. Let's say this scenario.
public class A{
private int id;
private String code;
private String name;
private List<B> bs;
}
public class B{
private int id;
private String code;
private String name;
private A a;
}
If you try to send to the view the class B or A with #ResponseBody it may cause an infinite loop. You can write a constructor in your class and create a query with your entityManager like this.
"select new A(id, code, name) from A"
This is the class with the constructor.
public class A{
private int id;
private String code;
private String name;
private List<B> bs;
public A(){
}
public A(int id, String code, String name){
this.id = id;
this.code = code;
this.name = name;
}
}
However, there are some constrictions about this solution, as you can see, in the constructor I did not make a reference to List bs this is because Hibernate does not allow it, at least in version 3.6.10.Final, so when I need to show both entities in a view I do the following.
public A getAById(int id); //THE A id
public List<B> getBsByAId(int idA); //the A id.
The other problem with this solution, is that if you add or remove a property you must update your constructor and all your queries.
In case you are using Spring Data Rest, issue can be resolved by creating Repositories for every Entity involved in cyclical references.
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.
The point is to place the #JsonIgnore in the setter method as follow. in my case.
Township.java
#Access(AccessType.PROPERTY)
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="townshipId", nullable=false ,insertable=false, updatable=false)
public List<Village> getVillages() {
return villages;
}
#JsonIgnore
#Access(AccessType.PROPERTY)
public void setVillages(List<Village> villages) {
this.villages = villages;
}
Village.java
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "townshipId", insertable=false, updatable=false)
Township township;
#Column(name = "townshipId", nullable=false)
Long townshipId;
I have faced same issue, add jsonbackref and jsonmanagedref and please make sure #override equals and hashCode methods , this definitely fix this issue.

Categories