Java – Spring JPA, MySQL- Could not write content: Infinite recursion StackOverflowError through reference chain

hibernatejavajpaspring

I am wondering if I am doing anything wrong here, Because I have done the loading type such a way that it is not recursive. But I get the following error. when I try to access the getServerRequest() method from the visit object.

WARN 6511 — [nio-8080-exec-2]
.w.s.m.s.DefaultHandlerExceptionResolver : Failed to write HTTP
message:
org.springframework.http.converter.HttpMessageNotWritableException:
Could not write content: Infinite recursion (StackOverflowError)
(through reference chain:
com.biscoind.domain.ServerRequest["visits"]->org.hibernate.collection.internal.PersistentBag[0]->com.biscoind.domain.Visit["request"]->com.biscoind.domain.ServerRequest["visits"]->org.hibernate.collection.internal.PersistentBag[0]->com.biscoind.domain.Visit["request"]->com.biscoind.domain.ServerRequest["visits"]->org.hibernate.collection.internal.PersistentBag[0]->com.biscoind.domain.Visit["request"]->com.biscoind.domain.ServerRequest["visits"]->org.hibernate.collection.internal.PersistentBag[0]->com.biscoind.domain.Visit["request"]->com.biscoind.domain.ServerRequest["visits"]->org.hibernate.collection.internal.PersistentBag[0]->com.biscoind.domain.Visit["request"]->com.biscoind.domain.ServerRequest["visits"]->org.hibernate.collection.internal.PersistentBag[0]->com.biscoind.domain.Visit["request"]->com.biscoind.domain.ServerRequest["visits"]->org.hibernate.collection.internal.PersistentBag[0]->com.biscoind.domain.Visit["request"]->com.biscoind.domain.ServerRequest["visits"]->org.hibernate.collection.internal.PersistentBag[0]->com.biscoind.domain.Visit["request"]->com.biscoind.domain.ServerRequest["visits"]->org.hibernate.collection.internal.PersistentBag[0]->

I have the following mapping classes,

@Entity
@Table(name = "server_request")
public class ServerRequest {

    private Long id;
    private Date createdDate;
    private Date lastUpdatedDate;

    private List<Visit> visits = new ArrayList<Visit>();

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "server_request_id")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "request", cascade = CascadeType.ALL)
    public List<Visit> getVisits() {
        return visits;
    }

    public void setVisits(List<Visit> visits) {
        this.visits = visits;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public Date getLastUpdatedDate() {
        return lastUpdatedDate;
    }

    public void setLastUpdatedDate(Date lastUpdatedDate) {
        this.lastUpdatedDate = lastUpdatedDate;
    }
}

and,

@Entity
@Table(name = "user_visit")
public class Visit implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;
    private ServerRequest request;
    private String status;
    private Date date;

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "user_visit_id")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "server_request_id", referencedColumnName = "server_request_id", nullable = false)
    public ServerRequest getRequest() {
        return request;
    }

    public void setRequest(ServerRequest request) {
        this.request = request;
    }

}

Best Solution

If you're using Jackson and it is JSON that your converter is trying to write (the stack doesn't make that clear), put @JsonIdentityInfo annotation on your getRequest() method.

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id", scope=ServerRequest.class)
@ManyToOne(fetch = FetchType.EAAGER)
@JoinColumn(name = "server_request_id", referencedColumnName = "server_request_id", nullable = false)
public ServerRequest getRequest() {
    return request;
}

This is the way Jackson can resolve cyclic dependencies in an object graph during serialization/deserialization.


Some tips:

  • You don't have to declare FetchType.LAZY. This is done by default.
  • On your ManyToOne, chage from FetchType.EAAGER to FetchType.EAGER
Related Question