3

I am trying out some very basic webservice. I get this exception everytime I try to return the Prtnr object.

Uncaught exception thrown in one of the service methods of the servlet: spitter. Exception thrown : 
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) 
(through reference chain: org.hibernate.collection.PersistentSet[0]->org.abc.dvo.PrtnrGeoInfo["id"]->org.abc.dvo.PrtnrGeoInfoId["partner"]->
org.abc.dvo.Prtnr["prtnrGeoInfos"]->org.hibernate.collection.PersistentSet[0]->org.abc.dvo.PrtnrGeoInfo["id"]->org.abc.dvo.PrtnrGeoInfoId["partner"]->
org.abc.dvo.Prtnr["prtnrGeoInfos"]->org.hibernate.collection.PersistentSet[0]->org.abc.dvo.PrtnrGeoInfo["id"]->org.abc.dvo.PrtnrGeoInfoId["partner"]->
org.abc.dvo.Prtnr["prtnrGeoInfos"]->org.hibernate.collection.PersistentSet[0]->org.abc.dvo.PrtnrGeoInfo["id"]->org.abc.dvo.PrtnrGeoInfoId["partner"]->
...
    at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:164)
    at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
    at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:446)
    at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:150)
    ...

The Prtnr class is :

public class Prtnr implements Cloneable, java.io.Serializable {

    private static final long serialVersionUID = 201207021420600052L;
    private Integer prtnrId;
    private String creatUserId;
    private Date creatTs;
    private String updtUserId;
    private Date updtTs;
    private String prtnrNm;
    private Integer cncilNum;
    private Character prtnrTypCd;
    private Set<PrtnrGeoInfo> prtnrGeoInfos = new HashSet<PrtnrGeoInfo>(0);
    private Set<PrtnrDtl> prtnrDtls = new HashSet<PrtnrDtl>(0);
    private Set<SuplyDtl> suplyDtls = new HashSet<SuplyDtl>(0);
    private Set<TrnsprtDtl> trnsprtDtls = new HashSet<TrnsprtDtl>(0);
    private Set<PrtnrFacil> prtnrFacils = new HashSet<PrtnrFacil>(0);
    private Set<PrtnrHumanResrc> prtnrHumanResrcs = new HashSet<PrtnrHumanResrc>(0);
    .....
    .....
    Getters and setters for these properties
    ...
}

The PrtnrGeoInfo class is :

public class PrtnrGeoInfo implements java.io.Serializable {
    private PrtnrGeoInfoId id = new PrtnrGeoInfoId();
    private String creatUserId;
    private Date creatTs;
    private String updtUserId;
    private Date updtTs;

    Getters and setters for these properties

}

The PrtnrGeoInfoId class is :

public class PrtnrGeoInfoId implements java.io.Serializable {   
    private Prtnr partner;
    private GeoSegment geoSegment;
    private static final long serialVersionUID = 201207060857580050L;

    Getters and setters for these properties
}

I believe it is because of the classes refrencing each other. But how can this problem be resolved. Within the app which is Struts 2 and Spring, this object get passed just fine.

The controller class is as follows:

@Controller
@RequestMapping("/partners")
public class PartnerController {
    @RequestMapping(value="/{id}", method=RequestMethod.GET, headers ={"Accept=text/xml,application/json"})
    @ResponseBody
    public Prtnr getPartner(@PathVariable("id") String id) throws Exception{
        Prtnr partner = null;
        try{
            partner = partnerService.getPartnerById(Integer.valueOf(id));
                System.out.println("******* Test message " );
        }catch(Exception ex){
            System.out.println("******* Exception thrown ... " + ex.getMessage());
        }

        return partner;
    }
}

The calling class is public class TestTemplate {

    private static final long serialVersionUID = 1130201273334264152L;
    public static void main(String[] args){
        Prtnr partner = (Prtnr)new RestTemplate().getForObject("http://localhost:9080/respondersApp/testWs/partners/{id}", Prtnr.class, "1");
        System.out.println("partner name is : " + partner.getPrtnrNm());
    }
}
user1860447
  • 1,316
  • 8
  • 25
  • 46

4 Answers4

6

In this link you can find how to solve this.

However below I'll paste the solution in practice.

It's very simple. Assuming that your database query already works without JSON, all you have to do is this:

Add the @JsonManagedReference In the forward part of the relationship (i.e. User.java class):

@Entity
public class User implements java.io.Serializable{

 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private long id;

 @Column(name="name")
 private String name;

 @ManyToMany
 @JoinTable(name="users_roles",joinColumns=@JoinColumn(name = "user_fk"),
 inverseJoinColumns=@JoinColumn(name = "role_fk"))
 @JsonManagedReference
 private Set<Role> roles = new HashSet<Role>();

...

Add the @JsonBackReference In the back part of the relationship (i.e. Role.java class):

@Entity
public class Role implements java.io.Serializable {

 @Id 
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 private int id;

 @ManyToMany(mappedBy="roles")
 @JsonBackReference
 private Set<User> users = new HashSet<User>();

...

The work is done. If you take a look at your firebug logs, you'll notice that the infinite recursive loop has disappeared.

madx
  • 6,723
  • 4
  • 55
  • 59
3

This is quite a common scenario for me when you are trying to convert entity classes into JSON format. The simplest solution is just to use @JsonIgnore on the reverse mapping to break the cycle.

tarka
  • 5,289
  • 10
  • 51
  • 75
  • If we use `@JsonIgnore` then we don't get associated entity data, how can we get that? – PAA Aug 06 '19 at 16:57
2

You can annotate the second reference of Prtnr in PrtnrGeoInfoId with @JsonBackReference

user788052
  • 143
  • 2
  • 8
-1

The infinite recursion is due to the following: Class Prtnr contains Set<PrtnrGeoInfo> prtnrGeoInfos and each PrtnrGeoInfo contains PrtnrGeoInfoId id which in turn contains Prtnr partner.

Thus, Prtnr -> PrtnrGeoInfo ->PrtnrGeoInfoId ->Prtnr, is causing a cyclic dependency which is a problem for Jackson when it is trying to do the POJO Mapping.

You need to remove this cyclic dependency to fix this exception.

Srinivas
  • 1,780
  • 1
  • 14
  • 27
  • Unfortunately that is the requirement of the app and cannot be removed. How can the cyclic dependency issue be overcome ? – user1860447 Jan 11 '13 at 16:02
  • I am guessing what you require perhaps in `PrtnrGeoInfoId` would be some instance variables of `Prtnr`. The only way to overcome that would be to remove `Prtnr` from `PrtnrGeoInfoId`. Also, think if you can replace that with some other Class. – Srinivas Jan 11 '13 at 16:20