robert trudel
robert trudel

Reputation: 5779

Infinite loop with spring-boot in a one to many relation

In a rest application, I use spring boot with jpa.

I have a class Lodger

who have

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "lodger")
private List<Reference> referenceList;

In my class Reference, i have

@ManyToOne
@JoinColumn(name = "lodgerId")
private Lodger lodger;

when i call this method

@RequestMapping(value = "/lodgers/{lodgerId}", method = RequestMethod.GET)
public Lodger getLogderById(@PathVariable("lodgerId") long lodgerId) {
    return lodgerService.getLodger(lodgerId);
}

I get this error

org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Infinite recursion (StackOverflowError) (through reference chain: server.bean.Lodger["referenceList"]->org.hibernate.collection.internal.PersistentBag[0]->server.bean.Reference["lodger"]->server.bean.Lodger["referenceList"]->org.hibernate.collection.internal.PersistentBag[0]->server.bean.Reference["lodger"]->server.bean.Lodger["referenceList"]...

Upvotes: 18

Views: 31884

Answers (7)

The only thing that you need is, in your class in which you have the annotation @ManyToOne, implement the next annotation with the attributes that you want to skip in the value section @JsonIgnoreProperties(value = {"yourAttribute", "handler", "hibernateLazyInitializer"}, allowSetters = true)

I put an example for your code ->

@ManyToOne(fetch = FetchType.LAZY)
@JsonIgnoreProperties(value = {"referenceList", "handler","hibernateLazyInitializer"}, allowSetters = true)
@JoinColumn(name = "lodgerId")
private Lodger lodger;

All the attributes that you put in the value section on the @JsonIgnoreProperties are ignored, with this you can resolve the infinite loop and use it for other developments with the same format in the future.

Upvotes: 3

Sankarasai
Sankarasai

Reputation: 73

@JsonIgnoreProperties({"hibernateLazyInitializer","referenceList"}) at class Level

For reference see this article on medium.com.

enter image description here

Upvotes: 0

VK321
VK321

Reputation: 5963

If you primary keys in both tables are same name for example : id.

Add this

@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
public class User {
    ...
}

And to Reference class.

@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
public class Reference {
    ...
}

Upvotes: 8

btd1337
btd1337

Reputation: 2994

Solution:

Use

@JsonManagedReference annotation for the first objects instantiated

@JsonBackReference annotation for the second objects instantiated

First:

@JsonManagedReference
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "lodger")
    private List<Reference> referenceList;

Second:

@JsonBackReference
@ManyToOne
    @JoinColumn(name = "lodgerId")
    private Lodger lodger;

Upvotes: 33

Safdar Akrami
Safdar Akrami

Reputation: 265

Lets assume your code looks like below :-

Lodger.class

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "lodger")
private List<Reference> referenceList;

public List<Reference> getReferenceList() {
    return referenceList;
}

public void setReferenceList(List<Reference> referenceList) {
    this.referenceList = referenceList;
}

@Override
public String toString() {
    return "Lodger[referenceList=" + referenceList + "]";
}

Reference.class

@ManyToOne
@JoinColumn(name = "lodgerId")
private Lodger lodger;

public Lodger getLodger() {
    return lodger;
}

public void setLodger(Lodger lodger) {
    this.lodger = lodger;
}

@Override
public String toString() {
    return "Reference[lodger=" + lodger + "]";
}

When you notice at the toString() method written in both the POJO's, you will see that we are calling toString() of both the classes from either side which results in infinite no. of calls to toString() method from both sides which never terminates. To avoid this situation remove any the reference from toString() of Refernce.class[You may remove from Lodger class also.] So toString() of Reference class will not have lodger property in it.

So finally your Reference class will look like :-

Reference.class

@ManyToOne
@JoinColumn(name = "lodgerId")
private Lodger lodger;

public Lodger getLodger() {
    return lodger;
}

public void setLodger(Lodger lodger) {
    this.lodger = lodger;
}

@Override
public String toString() {
    return "Reference[Properties other than lodger=" + properties other than lodger + "]";
}

Upvotes: 0

mdziob
mdziob

Reputation: 1176

Do not return entity with circular dependencies via REST webservice - create new DTO class, map entities fetched from database and return it in webservice.

More info here: http://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application

Of course if you want you may use another mapping library, my personal favourite is Orika (http://orika-mapper.github.io/orika-docs/intro.html)

Upvotes: 2

Mati
Mati

Reputation: 2576

It happens when you have a cycle in return object and spring tries to serialize it to other type.

Try to create DTO or Value Object (simple POJO) without cycles from returned model and then return it.

Upvotes: 6

Related Questions