Reputation: 759
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
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
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
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
Reputation: 18845
The issue is you have reference to Stock
s 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 Stock
s 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