Бесконечная рекурсия с выпуском Jackson JSON и Hibernate JPA - PullRequest
351 голосов
/ 24 июля 2010

При попытке преобразовать объект JPA, имеющий двунаправленную ассоциацию, в JSON, я продолжаю получать

org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)

Все, что я нашел, это этот поток , который в основном завершается рекомендациейизбегать двунаправленных ассоциаций.У кого-нибудь есть идеи для обхода этой весенней ошибки?

------ EDIT 2010-07-24 16:26:22 -------

Codesnippets:

Бизнес-объект 1:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class Trainee extends BusinessObject {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "id", nullable = false)
    private Integer id;

    @Column(name = "name", nullable = true)
    private String name;

    @Column(name = "surname", nullable = true)
    private String surname;

    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    private Set<BodyStat> bodyStats;

    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    private Set<Training> trainings;

    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    private Set<ExerciseType> exerciseTypes;

    public Trainee() {
        super();
    }

    ... getters/setters ...

Бизнес-объект 2:

import javax.persistence.*;
import java.util.Date;

@Entity
@Table(name = "ta_bodystat", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "id", nullable = false)
    private Integer id;

    @Column(name = "height", nullable = true)
    private Float height;

    @Column(name = "measuretime", nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date measureTime;

    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name="trainee_fk")
    private Trainee trainee;

Контроллер:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Controller
@RequestMapping(value = "/trainees")
public class TraineesController {

    final Logger logger = LoggerFactory.getLogger(TraineesController.class);

    private Map<Long, Trainee> trainees = new ConcurrentHashMap<Long, Trainee>();

    @Autowired
    private ITraineeDAO traineeDAO;

    /**
     * Return json repres. of all trainees
     */
    @RequestMapping(value = "/getAllTrainees", method = RequestMethod.GET)
    @ResponseBody        
    public Collection getAllTrainees() {
        Collection allTrainees = this.traineeDAO.getAll();

        this.logger.debug("A total of " + allTrainees.size() + "  trainees was read from db");

        return allTrainees;
    }    
}

JPA-реализация обучаемогоDAO:

@Repository
@Transactional
public class TraineeDAO implements ITraineeDAO {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public Trainee save(Trainee trainee) {
        em.persist(trainee);
        return trainee;
    }

    @Transactional(readOnly = true)
    public Collection getAll() {
        return (Collection) em.createQuery("SELECT t FROM Trainee t").getResultList();
    }
}

persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
             version="1.0">
    <persistence-unit name="RDBMS" transaction-type="RESOURCE_LOCAL">
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="validate"/>
            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
            <!-- <property name="dialect" value="org.hibernate.dialect.HSQLDialect"/>         -->
        </properties>
    </persistence-unit>
</persistence>

Ответы [ 19 ]

4 голосов
/ 28 мая 2018

Работает нормально для меня Устранить проблему бесконечной рекурсии Json при работе с Джексоном

Это то, что я сделал в OneToMany и ManyToOne Mapping

@ManyToOne
@JoinColumn(name="Key")
@JsonBackReference
private LgcyIsp Key;


@OneToMany(mappedBy="LgcyIsp ")
@JsonManagedReference
private List<Safety> safety;
4 голосов
/ 29 августа 2014

В моем случае было достаточно изменить отношение с:

@OneToMany(mappedBy = "county")
private List<Town> towns;

на:

@OneToMany
private List<Town> towns;

осталось другое отношение, как было:

@ManyToOne
@JoinColumn(name = "county_id")
private County county;
4 голосов
/ 02 мая 2018

@ JsonIgnoreProperties - это ответ.

Используйте что-то вроде этого: *

@OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
@JsonIgnoreProperties("course")
private Set<Student> students;
2 голосов
/ 27 марта 2018

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

вы получаете бесконечную рекурсию, поскольку класс BodyStat снова ссылается на объект Trainee

BodyStat

@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name="trainee_fk")
private Trainee trainee;

Стажер

@OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Column(nullable = true)
private Set<BodyStat> bodyStats;

Следовательно, вы должны комментировать / опустить вышеуказанную часть в Стажер

1 голос
/ 20 августа 2015

вы можете использовать DTO шаблон создания класса TraineeDTO без какой-либо аннотации hiberbnate, и вы можете использовать Джексон маппер, чтобы конвертировать Trainee в TraineeDTO и bingo сообщение об ошибке disapeare:)

1 голос
/ 14 марта 2018

Я тоже встречался с такой же проблемой.Я использовал @JsonIdentityInfo ObjectIdGenerators.PropertyGenerator.class тип генератора.

Это мое решение:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Trainee extends BusinessObject {
...
0 голосов
/ 20 апреля 2016

Если вы используете Spring Data Rest, проблему можно решить, создав репозитории для каждого объекта, участвующего в циклических ссылках.

0 голосов
/ 26 марта 2019

Если вы не можете игнорировать свойство, попробуйте изменить видимость поля. В нашем случае у нас был старый код, который по-прежнему отправлял объекты со связями, поэтому в моем случае это было исправлением:

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private Trainee trainee;
0 голосов
/ 30 ноября 2015

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

public class A{
   private int id;
   private String code;
   private String name;
   private List<B> bs;
}

public class B{
   private int id;
   private String code;
   private String name;
   private A a;
}

Если вы попытаетесь отправить в представление класс B или A с @ResponseBody, это может вызвать бесконечный цикл.Вы можете написать конструктор в своем классе и создать запрос с вашим entityManager следующим образом.

"select new A(id, code, name) from A"

Это класс с конструктором.

public class A{
   private int id;
   private String code;
   private String name;
   private List<B> bs;

   public A(){
   }

   public A(int id, String code, String name){
      this.id = id;
      this.code = code;
      this.name = name;
   }

}

Однако естьнекоторые ограничения этого решения, как вы можете видеть, в конструкторе я не делал ссылки на List bs , это потому, что Hibernate не разрешает это, по крайней мере в версии 3.6.10., поэтому, когда мне нужно показать обе сущности в виде, я делаю следующее:

public A getAById(int id); //THE A id

public List<B> getBsByAId(int idA); //the A id.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...