Не удалось записать JSON: бесконечная рекурсия (StackruError); вложенная исключительная пружинная загрузка
Это мой районный контроллер, когда я пытаюсь получить данные после сохранения, я получаю сообщение об ошибке, даже когда я пытаюсь получить объектную форму getDistrict(Long id) с такими же ударами, пожалуйста, предложите какой-то способ, я очень новая среда весны
package com.gad.services;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.gad.repositories.DistrictMasterRepositories;
import com.gad.rmodels.Districtmaster;
import com.gad.rmodels.Statemaster;
@Service
public class DistricMasterServices {
@Autowired
DistrictMasterRepositories districtMasterRepositories;
@Autowired
StateMasterServices stateMasterServices;
List<Districtmaster> districtmaster;
public Iterable<Districtmaster> savenewdistrict(Long id,Districtmaster districtmaster_rec){
System.out.println(id);
Statemaster statemaster=null;
statemaster = stateMasterServices.getStateById(id);
System.out.println("savenewdistrict");
districtmaster_rec.setStatemaster(statemaster);
districtMasterRepositories.save(districtmaster_rec);
Iterable<Districtmaster>districtmaster2 = districtMasterRepositories.findAll();
return districtmaster2;
}
public Districtmaster getDistrict(Long id){
Districtmaster districtmaster = districtMasterRepositories.findOne(id);
return districtmaster;
}
}
Модельный класс для гос.
package com.gad.rmodels;
import static javax.persistence.GenerationType.SEQUENCE;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
/**
* Statemaster generated by hbm2java
*/
@Entity
@Table(name="statemaster"
,schema="aop_gad_v1"
)
public class Statemaster implements java.io.Serializable {
private long id;
private String stateName;
private Set<Districtmaster> districtmasters = new HashSet<Districtmaster>(0);
public Statemaster() {
}
public Statemaster(long id) {
this.id = id;
}
public Statemaster(long id, String stateName, Set<Districtmaster> districtmasters) {
this.id = id;
this.stateName = stateName;
this.districtmasters = districtmasters;
}
@SequenceGenerator(name="generator_statemasterid", sequenceName="aop_gad_v1.gad_statemaster_seq")
@Id
@GeneratedValue(strategy=SEQUENCE, generator="generator_statemasterid")
@Column(name="id", unique=true, nullable=false)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
@Column(name="state_name", length=20)
public String getStateName() {
return this.stateName;
}
public void setStateName(String stateName) {
this.stateName = stateName;
}
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="statemaster")
public Set<Districtmaster> getDistrictmasters() {
return this.districtmasters;
}
public void setDistrictmasters(Set<Districtmaster> districtmasters) {
this.districtmasters = districtmasters;
}
}
Районная модель
package com.gad.rmodels;
import static javax.persistence.GenerationType.SEQUENCE;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
/**
* Districtmaster generated by hbm2java
*/
@SuppressWarnings("serial")
@Entity
@Table(name="districtmaster",schema="aop_gad_v1")
public class Districtmaster implements java.io.Serializable {
private long id;
private Statemaster statemaster;
private String districtName;
private Set<GadGuestHouseMaster> gadGuestHouseMasters = new HashSet<GadGuestHouseMaster>(0);
public Districtmaster() {
}
public Districtmaster(long id) {
this.id = id;
}
public Districtmaster(long id, Statemaster statemaster, String districtName, Set<GadGuestHouseMaster> gadGuestHouseMasters) {
this.id = id;
this.statemaster = statemaster;
this.districtName = districtName;
this.gadGuestHouseMasters = gadGuestHouseMasters;
}
@SequenceGenerator(name="generator_districtmasterid", sequenceName="aop_gad_v1.gad_districtmasterid_seq")
@Id
@GeneratedValue(strategy=SEQUENCE, generator="generator_districtmasterid")
@Column(name="id", unique=true, nullable=false)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="district_of_state")
public Statemaster getStatemaster() {
return this.statemaster;
}
public void setStatemaster(Statemaster statemaster) {
this.statemaster = statemaster;
}
@Column(name="district_name", length=20)
public String getDistrictName() {
return this.districtName;
}
public void setDistrictName(String districtName) {
this.districtName = districtName;
}
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="districtmaster")
public Set<GadGuestHouseMaster> getGadGuestHouseMasters() {
return this.gadGuestHouseMasters;
}
public void setGadGuestHouseMasters(Set<GadGuestHouseMaster> gadGuestHouseMasters) {
this.gadGuestHouseMasters = gadGuestHouseMasters;
}
}
Ошибка, которую я получаю
[{"timestamp":1512641978311,"status":200,"error":"OK","exception":"org.springframework.http.converter.HttpMessageNotWritableException","message":"Не удалось записать JSON: бесконечная рекурсия (StackruError); вложенным исключением является com.fasterxml.jackson.databind.JsonMappingException: бесконечная рекурсия (StackruError) (через цепочку ссылок: com.gad.rmodels.Statemaster[\"districtmasters\"]->org.hibernate.collection.internal.PersistentSet[0]-
12 ответов
Вы столкнулись с этой проблемой, потому что модель Statemaster содержит объект модели Districtmaster, который сам содержит объект модели Statemaster. Это вызывает бесконечную JSON-рекурсию.
Вы можете решить эту проблему тремя способами.
1 - Создайте DTO и включите только те поля, которые вы хотите отобразить в ответе.
2 - Вы можете использовать @JsonManagedReference
а также @JsonBackReference
аннотаций.
Например, добавить @JsonManagedReference
аннотация к модели Государственного мастера.
@JsonManagedReference
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="statemaster")
public Set<Districtmaster> getDistrictmasters() {
return this.districtmasters;
}
Добавить @JsonBackReference
аннотация к модели районного мастера.
@JsonBackReference
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="district_of_state")
public Statemaster getStatemaster() {
return this.statemaster;
}
3 - Вы можете использовать @JsonIgnore
аннотация на метод получения или метода получения.
@JsonIgnore
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="statemaster")
public Set<Districtmaster> getDistrictmasters() {
return this.districtmasters;
}
Тем не менее, этот подход пропустит набор Districtmaster из ответа.
Я боролся с той же проблемой в течение нескольких дней, я пробовал аннотации @JsonIgnore, @JsonManagedReference и @JsonBackReference, и даже аннотацию @JsonIdentityInfo, но ни одна из них не сработала.
Если вы (будущие читатели) окажетесь в той же ситуации, решение будет проще, чем вы ожидали, просто поместите @JsonIgnore или @JsonManagedReference / @JsonBackReference на получатель атрибута, а не на сам атрибут. И это будет делать.
Вот простой пример, как это сделать:
Скажем, у нас есть два класса, Order и Product, и отношение OneToMany между ними.
Класс заказа
public class Order{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private String id_order;
private double price;
@OneToMany(mappedBy = "order")
@LazyCollection(LazyCollectionOption.FALSE)
private List<Product> products
//constructor, getters & setter
}
Класс продукта:
public class Product{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private String id_product;
private String name;
@ManyToOne
@JoinColumn(name = "id_order")
private Order order;
//consturctor, getters & setters
}
Поэтому, чтобы использовать @JsonManagedReference и @JsonBackReference, просто добавьте их в получатели следующим образом:
public class Order{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private String id_order;
private double price;
@OneToMany(mappedBy = "order")
@LazyCollection(LazyCollectionOption.FALSE)
private List<Product> products
//constructor, getters & setter
@JsonManagedReference
public List<Product> getProducts(){
return products;
}
Класс продукта:
public class Product{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private String id_product;
private String name;
@ManyToOne
@JoinColumn(name = "id_order")
private Order order;
//consturctor, getters & setters
@JsonBackReference
public Order getOrder(){
return order;
}
}
@JsonBackReference и @JsonManagedReference у меня не работали, поскольку я использовал класс, представляющий таблицу соединений, и единственный способ заставить его работать - это поместить @JsonBackReference над полями, представляющими связанные классы. В итоге, если бы я извлекал запись из класса соединения с помощью JSON, ни одно из полей соединения не отображалось, что делало возвращаемую информацию бесполезной.
Я бы привел примеры, но избегаю длинных ответов. @JsonIdentityInfo, описанный в статье ниже, предоставляет короткое, простое, но очень эффективное решение.
Это потому что для Statemaster
в JSON множество Districtmaster
это ставится. И каждый Districtmaster
имеет Statemaster
сам по себе, так что это также положить в JSON. Так что вы получите бесконечную рекурсию
@JsonIgnore
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY,
mappedBy="statemaster")
public Set<Districtmaster> getDistrictmasters() {
return this.districtmasters;
}
Добавление @JsonIgnore
аннотация на Set<Districtmaster>
предотвратит эту рекурсию. Вы можете поставить @JsonIgnore
в public Statemaster getStatemaster()
или.
Эта проблема возникает при работе с двунаправленным отображением. использование @JsonManagedReferenc
а также @JsonBackReference
в классе DAO/Entity
@JsonManagedReference - это передняя часть ссылки, которая обычно сериализуется.@JsonBackReference является задней частью ссылки - она будет исключена из сериализации.
В Statemaster
класс сделать districtmasters
а List<Districtmaster>
вместо того Set<Districtmaster>
и соответственно измените метод получения: public List<Districtmaster> getDistrictmasters()
. К сожалению, я не могу объяснить почему, но у меня это сработало.
Сообщение об ошибке, которое я получил в своем проекте
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion
В моем случае я также столкнулся с этой же проблемой, чтобы решить эту проблему, я просто использую @JsonIgnore, и она работает нормально. Затем я заменил @JsonIgnore на @JsonBackReference, и это тоже сработало отлично.
Чтобы понять сценарий, проверьте проект, супруга, адрес и класс сотрудников.
Класс адреса
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Getter
@Setter
@ToString
@NoArgsConstructor
@EqualsAndHashCode
@Entity
@Table(name = "address")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String line1;
private String line2;
private String zipCode;
private String city;
private String state;
private String country;
@JsonIgnore
//@JsonBackReference
@ManyToOne
private Employee employee;
public Address(String line1, String line2, String zipCode, String city, String state, String country, Employee employee) {
this.line1 = line1;
this.line2 = line2;
this.zipCode = zipCode;
this.city = city;
this.state = state;
this.country = country;
this.employee = employee;
}
}
Класс сотрудника
@Getter
@Setter
@NoArgsConstructor
@ToString
@EqualsAndHashCode
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID", unique = true, nullable = false)
private Integer employeeId;
private String employeeName;
private String employeeCity;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "fk_spouse")
private Spouse spouse;
@OneToMany(cascade = CascadeType.ALL)
private List<Address> addresses;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "employee_project",
joinColumns = @JoinColumn(name = "fk_employee"),
inverseJoinColumns = @JoinColumn(name = "fk_project"))
private List<Project> projects;
public Employee(String employeeName, String employeeCity, Spouse spouse, List<Address> addresses, List<Project> projects) {
this.employeeName = employeeName;
this.employeeCity = employeeCity;
this.spouse = spouse;
this.addresses = addresses;
this.projects = projects;
}
public void removeProject(Project project){
this.projects.remove(project);
project.getEmployees().remove(project);
}
public void addProject(Project project){
this.projects.add(project);
project.getEmployees().add(this);
}
}
Класс проекта
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import javax.persistence.*;
import java.util.List;
@Getter
@Setter
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@Entity
@Table(name = "project")
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String clientName;
@JsonIgnore
// @JsonBackReference
@ManyToMany(mappedBy = "projects")
private List<Employee> employees;
public Project(String name, String clientName, List<Employee> employees) {
this.name = name;
this.clientName = clientName;
this.employees = employees;
}
}
Супруг Класс
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import javax.persistence.*;
@Getter
@Setter
@NoArgsConstructor
@ToString
@EqualsAndHashCode
@Entity
@Table(name = "spouse")
public class Spouse {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String mobileNo;
private Long age;
// @JsonBackReference
@JsonIgnore
@OneToOne(mappedBy = "spouse")
private Employee employee;
public Spouse(String name, String mobileNo, Long age, Employee employee) {
this.name = name;
this.mobileNo = mobileNo;
this.age = age;
this.employee = employee;
}
}
Я знаю, что @JsonIgnore и @JsonManagedReference, @JsonBackReference используются для решения бесконечной рекурсии (StackOverflowError)
Отвечать
Это аннотации Джексона.
@JsonIgnore не создан для решения проблемы бесконечной рекурсии (StackOverflowError), а только игнорирует свойство сериализации и десериализации. Просто избегайте бесконечной рекурсии.
Кроме того, @JsonManagedReference и @JsonBackReference созданы для обработки двусторонней связи между полями. Если вам не нужны эти свойства в процессе сериализации или десериализации, вы можете использовать @JsonIgnore. В противном случае лучше использовать пару @JsonManagedReference /@JsonBackReference.
Если вы используете Ломбок:
Проблема может быть связана с использованием
Lombok
и Установить вместе.
Lombok создает метод, который используется в Java для определения того, являются ли два объекта одинаковыми или нет. Метод, сгенерированный Lombok, вызывает метод другого класса, который, в свою очередь, вызывает метод первого класса, создавая тем самым бесконечную рекурсию.
Для решения задачи нужно либо использовать
List
вместо
Set
, или переопределить
equals
а также
hashCode
методы (поэтому Lombok не будет их генерировать).
Я узнал об этом из другого вопроса StackOverflow .
Есть 2 разных способа решить проблему:
- Вам нужно
implement Serializable
на всех@Data
классы / DTOS используется, и убедитесь , что генерироватьprivate static final long serialVersionUID
для класса. Преимущество: таким образом вы можете использовать аннотации " lombok " @Data / @Getter / @Setter.
- Поместите @JsonIgnore в каждый получатель / сеттер во всех используемых классах / DTO. Недостаток: использование аннотаций @Data / @Getter / @Setter " ломбок " может быть проблематичным.
Если вы используете @JsonIgnore, проверьте, правильно ли он импортирован. Он должен быть импортирован из
import com.fasterxml.jackson.annotation.JsonIgnore;
Это решает для меня.
Похоже, ваша проблема в спящих отношениях. Когда вы пытаетесь сериализовать сущность Statemaster, сериализатор вызывает сериализацию набора Districtmaster. Который в свою очередь как-то снова ссылается на Государственного Мастера.
Есть два возможных способа решения проблемы: 1. Прокси-объект
- Создать DTO (Data Transfer Object) - вид вашей сущности, в котором должны быть назначены все необходимые поля и возвращен DTO.