0

I'm trying to create a REST service with Spring. Everything works until I try to add a List of object (CartItem) to my main object (Cart).

This is my main object

@Entity
@Table(name="cart")
public class Cart implements Serializable{
   ...
   @GeneratedValue(strategy=GenerationType.IDENTITY)
   @Id
   @Column(name="id")
   private Integer id;  

   /*when I add this I get the error. If I remove this, the 
   REST service works*/
   @OneToMany(mappedBy="cart", fetch = FetchType.EAGER)
   private List<CartItem> cartItems; 

   //getter, setter, constructors, other fields ecc.
}

This is the object inside the List:

@Entity
@Table(name="cart_item")
public class CartItem implements Serializable{
   ...
   @GeneratedValue(strategy=GenerationType.IDENTITY)
   @Id
   @Column(name="id")
   private Integer id; 

   @OneToOne(targetEntity = Product.class, cascade = CascadeType.ALL)
   @JoinColumn(referencedColumnName="productId", name="product_id" )    
   private Product product;     

   @ManyToOne 
   @JoinColumn(name="cart_id", nullable=false)     
   private Cart cart;

  //getter, setter, constructors, other fields ecc.
}

This is my controller

@RestController
@RequestMapping(value="rest/cart")
public class CartRestController {
  ...
    @RequestMapping(value = "/", method = RequestMethod.GET) 
    public List<Cart> readAll() { 
        return cartService.read(); 
    }
 ...
}

I get this error:

SEVERE: Servlet.service() for servlet [dispatcher] in context with path 
[/webstore] threw exception [Request processing failed; nested exception
 is org.springframework.http.converter.HttpMessageNotWritableException: 
Could not write JSON: Infinite recursion (StackOverflowError); nested
 exception is com.fasterxml.jackson.databind.JsonMappingException: 
Infinite recursion (StackOverflowError) (through reference chain:...

I suppose that I had to manage the List inside the Cart object in a particular manner, maybe because i'm using JPA, but I still didn't find a solution on the internet. Can anyone help me?

MDP
  • 4,177
  • 21
  • 63
  • 119

2 Answers2

2

This is a serialization recursion problem, it happens because CartItem has a bidirectional mapping back to Cart. So what happens is that

  • a Cart gets serialized to JSON
    • all the CartItems inside it get serialized to JSON
      • the Cart property inside CartItem get serialized to JSON
        • the CartItems inside the cart get serialized to json, etc. etc.

You will probably want to exclude the CartItem.cart field from serialization by marking it with the @JsonIgnore annotation.


It is only too easy to expose far too much information to the outside world if you use JPA entities directly inside your webservices. Jackson actually has a useful feature called a JsonView which allows you to define which properties get exposed, you can even tailor it per webservice call if you want.

Gimby
  • 5,095
  • 2
  • 35
  • 47
1

Never ending list? Did you mean a stackOverFlow exception?

If the situation is just like I said,then you should check something like fetch type and the entities' toString() or equal() method or something like that.

For example,there are to entities named A and B and their relationship is one to many(A is the one).If you config both of their fetchType as Eager,then when jpa query A,it will query B too.But B also contains A,so jpa will query A again.This kind of circle loop will cause a stackOverFlow.

By the way, how about providing more info about your problem like the Exception name?It's too hard for me to give you a specific solution,All I can do is to tell you some experiences I have met before.


Well,I created a small project with SpringBoot 2.1.0 and MySql.

It's my cartItem

public class CartItem {
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Id
    @Column(name="id")
    private Integer id;

    @JsonIgnore
    @ManyToOne
    @JoinColumn(name="cart_id", nullable=false)
    private Cart cart;
}

and my cart:

public class Cart {
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    @Id
    @Column(name="id")
    private Integer id;

    @OneToMany(mappedBy="cart", fetch = FetchType.EAGER)
    private List<CartItem> cartItems;
}

Controller is as same as you wrote.After adding a @JsonIgnore to cart filed of CartItem,circle loop is over(before i do that,the program did had a circle loop problem).

Every time you use jpa with @oneToMany,@ManyToOne or @ManyToMany,you should be careful about this problem.This circular reference case could happen when instantiating a object, printing a object or something like this.And of course there is a lot of way to solve it like changing fetch type to LAZY,adding @JsonIgnore,overriding toString() and equal() method.

AokoQin
  • 159
  • 4
  • Hi @AokoQin, I edited my post, now the problem should be clearer. The problem is solved by adding JsonIgnore in the class CartItem, on the field that mapped back the Cart class, as suggested by the user Gimby. As you said, it was a cirlce loop that causes the error. – MDP Nov 22 '18 at 09:19