swv
swv

Reputation: 759

Infinite Recursion with Jackson JSON and Hibernate JPA issue (yet another)

I've found oodles of resources on this, such as this one - Infinite Recursion with Jackson JSON and Hibernate JPA issue. I've tried to implement all of the various suggestions described there (including basic @JsonIgnore), but to no avail. I can't get anything other than infinite recursion errors regardless of what I try. I think I have a pretty similar/typical setup, but obviously with something wrong since despite using @JsonManagedReference, @JsonBackReferencere and @JsonIdentityInfo annotations, I keep getting the error.

My tables are "exchange" and "stock", with a manytoMany between them, and I've been testing by via ExchangeEndpoint. I've confirmed that if I completely remove "stock" from "exchange" entity, the service works fine, but for some reason, the json annotations do not seem to have any affect. Below is what I think is the solution based on the second (but more popular) answer in the aforementioned Infinite Recursion with Jackson JSON and Hibernate JPA issue.

Exchange.java

@Entity
@Table(name = "exchange", schema = "public")
@XmlRootElement
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public class Exchange implements java.io.Serializable {
...
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "exchange")
    @JsonManagedReference
    public Set<Stock> getStocks() {
        return this.stocks;
    }
...

Stock.java

@Entity
@Table(name = "stock", schema = "public")
@XmlRootElement
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public class Stock implements java.io.Serializable {
...
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "exchangeid", nullable = false)
    @JsonBackReference
    @JsonIgnore
    public Exchange getExchange() {
        return this.exchange;
    }
...

ExchangeEndpoint.java

@Stateless
@Path("/exchanges")
public class ExchangeEndpoint {
    @PersistenceContext(unitName = "postgresql-ss4")
    private EntityManager em;
...
    @GET
    @Produces("application/json")
    public List<Exchange> listAll(@QueryParam("start") Integer startPosition,
            @QueryParam("max") Integer maxResult) {
        TypedQuery<Exchange> findAllQuery = em
                .createQuery(
                        "SELECT DISTINCT e "
                        + "FROM Exchange e "
                        + "LEFT JOIN FETCH e.stocks "
                        + "ORDER BY e.exchangeid",
                        Exchange.class);
        if (startPosition != null) {
            findAllQuery.setFirstResult(startPosition);
        }
        if (maxResult != null) {
            findAllQuery.setMaxResults(maxResult);
        }
        final List<Exchange> results = findAllQuery.getResultList();
        return results;
    }

edit:

some of the error output to be sure i'm not misinterpreting something;

15:35:16,406 ERROR [org.jboss.resteasy.resteasy_jaxrs.i18n] (http-/0.0.0.0:8080-1) RESTEASY000100: Failed executing GET /exchanges/: org.jboss.resteasy.spi.WriterException: org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: net.hb.forge2RestServices.model.Exchange["stocks"]->org.hibernate.collection.internal.PersistentSet[0]->net.hb.forge2RestServices.model.Stock["exchange"]->net.hb.forge2RestServices.model.Exchange["stocks"]->org.hibe ... ... Exchange["stocks"]->org.hibernate.collection.internal.PersistentSet[0]->net.hb.forge2RestServices.model.Stock["exchange"]->net.hb.forge2RestServices.model.Exchange["stocks"]) at org.jboss.resteasy.core.ServerResponse.writeTo(ServerResponse.java:262)

Please let me know what additional information I can provide to help explain my debacle. tiy.

Upvotes: 7

Views: 8682

Answers (4)

Naami
Naami

Reputation: 358

Use @JsonManagedReference, @JsonBackReference

public class User {
   public int id;
   public String name;

   @JsonBackReference
   public List<Item> userItems;
}


public class Item {
    public int id;
    public String itemName;

    @JsonManagedReference
    public User owner;
}

Note that:

@JsonManagedReference is the forward part of reference – the one that gets serialized normally. @JsonBackReference is the back part of reference – it will be omitted from serialization.

http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion

Upvotes: 2

swv
swv

Reputation: 759

It seems that the problem was with imports. I'm not sure why, but org.codehaus.jackson.annotate.JsonIgnore works for me, and com.fasterxml.jackson.annotation.JsonIgnore doesn't.

Upvotes: 2

Ga&#235;l Marziou
Ga&#235;l Marziou

Reputation: 16294

This kind of issues is much more easily avoided if you don't expose your JPA entities through REST.

You should consider using DTOs on REST side and mapping entities to DTOs and the reverse either by coding a mapper manually or by generating one using MapStruct.

As a bonus you'll avoid security issues.

Upvotes: 3

Zbynek Vyskovsky - kvr000
Zbynek Vyskovsky - kvr000

Reputation: 18845

The issue is you have reference to Stocks and back from Stock you have reference to Exchange. Therefore JSON goes to stock, back to Exchange and then for Exchange it will generate to all Stocks and so on.

To break the cycle it's best to put JsonIgnore annotation to the field which references the parent object - ManyToOne - in this case it's Stock.exchange field (or the getter for that).

Upvotes: 3

Related Questions