Не удалось записать 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 является задней частью ссылки - она ​​будет исключена из сериализации.

(Прочитайте больше)

Используйте List вместо Set. Вот как я решил свою проблему.

В 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)

Отвечать

Это аннотации Джексона.

  1. @JsonIgnore не создан для решения проблемы бесконечной рекурсии (StackOverflowError), а только игнорирует свойство сериализации и десериализации. Просто избегайте бесконечной рекурсии.

  2. Кроме того, @JsonManagedReference и @JsonBackReference созданы для обработки двусторонней связи между полями. Если вам не нужны эти свойства в процессе сериализации или десериализации, вы можете использовать @JsonIgnore. В противном случае лучше использовать пару @JsonManagedReference /@JsonBackReference.

Если вы используете Ломбок:

Проблема может быть связана с использованием Lombokи Установить вместе.

Lombok создает метод, который используется в Java для определения того, являются ли два объекта одинаковыми или нет. Метод, сгенерированный Lombok, вызывает метод другого класса, который, в свою очередь, вызывает метод первого класса, создавая тем самым бесконечную рекурсию.

Для решения задачи нужно либо использовать Listвместо Set, или переопределить equalsа также hashCodeметоды (поэтому Lombok не будет их генерировать).

Я узнал об этом из другого вопроса StackOverflow .

Есть 2 разных способа решить проблему:


  1. Вам нужно implement Serializable на всех @Dataклассы / DTOS используется, и убедитесь , что генерироватьprivate static final long serialVersionUIDдля класса. Преимущество: таким образом вы можете использовать аннотации " lombok " @Data / @Getter / @Setter.

  1. Поместите @JsonIgnore в каждый получатель / сеттер во всех используемых классах / DTO. Недостаток: использование аннотаций @Data / @Getter / @Setter " ломбок " может быть проблематичным.

Если вы используете @JsonIgnore, проверьте, правильно ли он импортирован. Он должен быть импортирован из

      import com.fasterxml.jackson.annotation.JsonIgnore;

Это решает для меня.

Похоже, ваша проблема в спящих отношениях. Когда вы пытаетесь сериализовать сущность Statemaster, сериализатор вызывает сериализацию набора Districtmaster. Который в свою очередь как-то снова ссылается на Государственного Мастера.

Есть два возможных способа решения проблемы: 1. Прокси-объект

  1. Создать DTO (Data Transfer Object) - вид вашей сущности, в котором должны быть назначены все необходимые поля и возвращен DTO.
Другие вопросы по тегам