Как использовать @RestController (Spring) с дочерним списком объектов

Я пытаюсь создать службу REST с помощью Spring. Все работает, пока я не попытаюсь добавить список объектов (CartItem) в мой основной объект (Cart).

Это мой главный объект

@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.
}

Это объект внутри списка:

@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.
}

Это мой контроллер

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

Я получаю эту ошибку:

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 (StackruError); nested
 exception is com.fasterxml.jackson.databind.JsonMappingException: 
Infinite recursion (StackruError) (through reference chain:...

Я предполагаю, что мне нужно было управлять списком внутри объекта Cart определенным образом, возможно, потому что я использую JPA, но я все еще не нашел решения в Интернете. Может кто-нибудь мне помочь?

2 ответа

Решение

Это проблема рекурсии сериализации, потому что CartItem имеет двунаправленное отображение обратно в корзину. Так что же происходит, что

  • корзина сериализуется в JSON
    • все CartItems внутри него сериализуются в JSON
      • свойство Cart внутри CartItem получает сериализацию в JSON
        • элементы CartItems внутри корзины сериализуются в JSON и т. д. и т. д.

Возможно, вы захотите исключить поле CartItem.cart из сериализации, пометив его @JsonIgnore аннотаций.


Слишком легко представить слишком много информации внешнему миру, если вы используете сущности JPA непосредственно внутри своих веб-сервисов. У Джексона на самом деле есть полезная функция, которая называется JsonView, которая позволяет вам определять, какие свойства будут доступны, вы даже можете настроить их для каждого вызова веб-службы, если хотите.

Бесконечный список? Вы имели в виду исключение stackOverFlow?

Если ситуация такая же, как я сказал, тогда вы должны проверить что-то типа fetch и метод toString() или equal() сущностей или что-то в этом роде.

Например, существуют сущности с именами A и B, и их отношение одно ко многим (A - единственное). Если вы сконфигурируете оба их fetchType как Eager, то когда jpa запросит A, он также запросит B. Но B также содержит A, поэтому jpa снова будет запрашивать A. Этот тип циклического цикла вызовет stackOverFlow.

Кстати, как насчет предоставления дополнительной информации о вашей проблеме, такой как имя исключения? Мне слишком сложно дать вам конкретное решение, все, что я могу сделать, это рассказать вам о некоторых событиях, с которыми я встречался раньше.


Ну, я создал небольшой проект с SpringBoot 2.1.0 и MySql.

Это моя корзина

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;
}

и моя корзина:

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

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

Контроллер такой же, как вы написали. После добавления @JsonIgnore в корзину, заполненную CartItem, круговой цикл завершен (до того, как я это сделал, в программе возникла проблема с круговым циклом).

Каждый раз, когда вы используете jpa с @oneToMany,@ManyToOne или @ManyToMany, вы должны быть осторожны с этой проблемой. Этот случай круговой ссылки может произойти при создании экземпляра объекта, печати объекта или чего-то подобного. И, конечно же, способ решить эту проблему, например, изменить тип выборки на LAZY, добавить @JsonIgnore, переопределить методы toString() и equal().

Другие вопросы по тегам