Infinite Recursion with Jackson JSON and Hibernate JPA issue


412

Próbuję przekonwertować obiekt JPA, który ma dwukierunkowe powiązanie na JSON, ciągle się pojawia

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

Znalazłem tylko ten wątek, który zasadniczo kończy się zaleceniem unikania skojarzeń dwukierunkowych. Czy ktoś ma pomysł na obejście tego wiosennego błędu?

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

Fragmenty kodu:

Obiekt biznesowy 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 ...

Obiekt biznesowy 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;

Kontroler:

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

Wdrożenie JPA stażysty 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>

Dodaj @Transientdo Trainee.bodyStats.
phil294

Od 2017 roku @JsonIgnorePropertiesjest najczystszym rozwiązaniem. Sprawdź odpowiedź Zammel AlaaEddine jest więcej szczegółów.
Utku

Jaka jest wina tej wiosny?
Nathan Hughes

Odpowiedzi:


293

Możesz użyć, @JsonIgnoreaby przerwać cykl.


1
@Ben: Właściwie to nie wiem. Być może jego obsługa nie została włączona: wiki.fasterxml.com/JacksonJAXBAnnotations
axtavt

40
Od wersji Jackson 1.6 istnieje lepsze rozwiązanie: możesz użyć dwóch nowych adnotacji, aby rozwiązać nieskończony problem rekurencji bez ignorowania programów pobierających / ustawiających podczas serializacji. Zobacz moją odpowiedź poniżej, aby uzyskać szczegółowe informacje.
Kurt Bourbaki,

1
@axtavt Dzięki za idealną odpowiedź. Przy okazji, przyjechałem z innym rozwiązaniem: możesz po prostu uniknąć tworzenia gettera dla tej wartości, więc Spring nie będzie mógł uzyskać do niego dostępu podczas tworzenia JSON (ale nie sądzę, że pasuje do każdego przypadku, więc twoja odpowiedź jest lepsza)
Siemion Daniłow

11
Wszystkie powyższe rozwiązania wydają się wymagać zmiany obiektów domeny przez dodanie adnotacji. Jeśli serializuję zajęcia innych podmiotów, nie mam możliwości ich modyfikacji. Jak mogę uniknąć tego problemu?
Jianwu Chen

2
to rozwiązanie nie działa w niektórych sytuacjach. W relacyjnej bazie danych z jpa, jeśli wstawisz, będziesz zerować @JsonIgnorew „kluczu obcym” podczas aktualizacji encji ...
slim

628

JsonIgnoreProperties [aktualizacja 2017]:

Możesz teraz użyć JsonIgnoreProperties, aby ukryć serializację właściwości (podczas serializacji) lub zignorować przetwarzanie odczytanych właściwości JSON (podczas deserializacji) . Jeśli nie tego szukasz, czytaj dalej poniżej.

(Dzięki As Zammel AlaaEddine za zwrócenie na to uwagi).


JsonManagedReference i JsonBackReference

Od wersji Jackson 1.6 możesz użyć dwóch adnotacji, aby rozwiązać nieskończony problem rekurencji bez ignorowania programów pobierających / ustawiających podczas serializacji: @JsonManagedReferencei @JsonBackReference.

Wyjaśnienie

Aby Jackson działał dobrze, jedna z dwóch stron relacji nie powinna być serializowana, aby uniknąć pętli nieskończoności, która powoduje błąd przepełnienia stosu.

Tak więc Jackson bierze do przodu część referencji (twoja Set<BodyStat> bodyStatsklasa Trainee) i konwertuje ją na format pamięci podobny do jsona; jest to tak zwany proces gromadzenia . Następnie Jackson szuka tylnej części referencji (tj. Trainee traineeW klasie BodyStat) i pozostawia ją taką, jaka jest, nie serializując jej. Ta część relacji zostanie przebudowana podczas deserializacji ( odblokowania ) odwołania do przodu.

Możesz zmienić swój kod w ten sposób (pomijam niepotrzebne części):

Obiekt biznesowy 1:

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

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

Obiekt biznesowy 2:

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

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

Teraz wszystko powinno działać poprawnie.

Jeśli chcesz uzyskać więcej informacji, napisałem artykuł o problemach Json i Jackson Stackoverflow na moim blogu Keenformatics .

EDYTOWAĆ:

Inną przydatną adnotacją, którą można sprawdzić, jest @JsonIdentityInfo : używając go, za każdym razem, gdy Jackson serializuje Twój obiekt, doda do niego identyfikator (lub inny wybrany atrybut), aby za każdym razem nie „skanował” go ponownie. Może to być przydatne, gdy istnieje pętla łańcuchowa między bardziej powiązanymi obiektami (na przykład: Zamówienie -> Linia zamówienia -> Użytkownik -> Zamówienie i ponownie).

W takim przypadku musisz zachować ostrożność, ponieważ może być konieczne odczytanie atrybutów obiektu więcej niż jeden raz (na przykład na liście produktów zawierającej więcej produktów, które mają tego samego sprzedawcę), a ta adnotacja nie pozwala tego zrobić. Sugeruję, aby zawsze sprawdzać dzienniki firebug, aby sprawdzić odpowiedź Jsona i zobaczyć, co się dzieje w kodzie.

Źródła:


29
Dziękuję za jasną odpowiedź. Jest to wygodniejsze rozwiązanie niż @JsonIgnorepowrót do referencji.
Utku Özdemir

3
To zdecydowanie właściwy sposób, aby to zrobić. Jeśli zrobisz to w ten sposób po stronie serwera, ponieważ używasz tam Jacksona, nie ma znaczenia, jakiego programu mapującego json używasz po stronie klienta i nie musisz ustawiać podrzędnego podręcznika łącza nadrzędnego. To po prostu działa. Dzięki Kurt
flosk8,

1
Ładne, szczegółowe wyjaśnienie oraz zdecydowanie lepsze i bardziej opisowe podejście niż @JsonIgnore.
Piotr Nowicki

2
Dzięki! @JsonIdentityInfo pracował dla cyklicznych odwołań, które obejmowały wiele jednostek w wielu nakładających się pętlach.
n00b

1
Nie mogę sprawić, żeby działało przez całe moje życie. Wydaje mi się, że mam dość podobną konfigurację, ale najwyraźniej mam coś złego, ponieważ nie mogę uzyskać niczego oprócz nieskończonych błędów rekurencyjnych:
swv

103

Nowa adnotacja @JsonIgnoreProperties rozwiązuje wiele problemów z innymi opcjami.

@Entity

public class Material{
   ...    
   @JsonIgnoreProperties("costMaterials")
   private List<Supplier> costSuppliers = new ArrayList<>();
   ...
}

@Entity
public class Supplier{
   ...
   @JsonIgnoreProperties("costSuppliers")
   private List<Material> costMaterials = new ArrayList<>();
   ....
}

Sprawdź to tutaj. Działa tak jak w dokumentacji:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html


@tero - Przy takim podejściu nie otrzymujemy danych powiązanych z bytem.
Pra_A,

@ PAA HEY PAA Myślę, że jest to związane z bytem! dlaczego tak mówisz ?
tero17

1
@ tero17 jak zarządzasz nieskończoną rekurencją, gdy masz więcej niż 2 klasy? Na przykład: Klasa A -> Klasa B -> Klasa C -> Klasa A. Próbowałem z JsonIgnoreProperties bez powodzenia
Villat

@Villat to kolejny problem do rozwiązania, sugeruję otworzyć na to nowe zapotrzebowanie.
tero17

+1 dla próbki kodu, jako początkujący Jackson, użycie @JsonIgnoreProperties nie było całkowicie jasne, czytając JavaDoc
Wecherowski

47

Możesz także użyć Jackson 2.0+ @JsonIdentityInfo. Działa to znacznie lepiej dla moich klas hibernacji niż @JsonBackReferencei @JsonManagedReference, które miały dla mnie problemy i nie rozwiązały problemu. Po prostu dodaj coś takiego:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@traineeId")
public class Trainee extends BusinessObject {

@Entity
@Table(name = "ta_bodystat", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@bodyStatId")
public class BodyStat extends BusinessObject {

i powinno działać.


Czy możesz wyjaśnić „To działało znacznie lepiej”? Czy występuje problem z zarządzanym odwołaniem?
Utku Özdemir

@ UtkuÖzdemir Dodałem szczegóły na temat @JsonIdentityInfopowyższej odpowiedzi.
Kurt Bourbaki,

1
jest to najlepsze rozwiązanie, jakie do tej pory znaleźliśmy, ponieważ gdy użyliśmy „@JsonManagedReference”, metoda get zwróciła wartości bez żadnego błędu przepełnienia stosu. Ale kiedy próbowaliśmy zapisać dane za pomocą postu, zwrócił błąd 415 (nieobsługiwany błąd mediów)
cuser 16.01.2014

1
Dodałem @JsonIdentityInfoadnotację do moich bytów, ale to nie rozwiązuje problemu rekurencji. Tylko @JsonBackReferencei @JsonManagedReferencerozwiązuje, ale są odwzorowywane usuń właściwości z JSON.
Oleg Abrazhaev

19

Ponadto Jackson 1.6 obsługuje obsługę dwukierunkowych odniesień ... co wydaje się być tym, czego szukasz ( ten wpis na blogu również wspomina o tej funkcji)

Od lipca 2011 r. Istnieje także „ jackson-module-hibernacja ”, który może pomóc w niektórych aspektach radzenia sobie z obiektami Hibernacji, choć niekoniecznie w tym konkretnym (który wymaga adnotacji).


Linki są martwe, czy masz coś przeciwko aktualizacji lub edycji odpowiedzi.
GetMeARemoteJob


9

Dla mnie to działało idealnie. Dodaj adnotację @JsonIgnore do klasy podrzędnej, w której wspominasz o odwołaniu do klasy nadrzędnej.

@ManyToOne
@JoinColumn(name = "ID", nullable = false, updatable = false)
@JsonIgnore
private Member member;

2
Myślę, że @JsonIgnoreignoruje ten atrybut przed pobraniem po stronie klienta. Co jeśli potrzebuję tego atrybutu z jego dzieckiem (jeśli ma dziecko)?
Khasan 24-7

6

Jest teraz moduł Jacksona (dla Jackson 2) specjalnie zaprojektowany do obsługi leniwych problemów z inicjalizacją Hibernacji podczas serializacji.

https://github.com/FasterXML/jackson-datatype-hibernate

Po prostu dodaj zależność (zauważ, że dla Hibernacji 3 i Hibernacji 4 istnieją różne zależności):

<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-hibernate4</artifactId>
  <version>2.4.0</version>
</dependency>

a następnie zarejestruj moduł podczas inicjalizacji ObjectMapper Jacksona:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate4Module());

Dokumentacja obecnie nie jest świetna. Zobacz kod Hibernate4Module, aby uzyskać dostępne opcje.


Problem rozwiązuje się wtedy, ponieważ wygląda interesująco. Mam ten sam problem co OP i wszystkie sztuczki, w tym powyższe, nie zadziałały.
user1567291

6

Działa dobrze dla mnie Rozwiąż problem z Json Infinite Recursion podczas pracy z Jacksonem

Tak zrobiłem w mapowaniu oneToMany i ManyToOne

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


@OneToMany(mappedBy="LgcyIsp ")
@JsonManagedReference
private List<Safety> safety;

Użyłem mapowania hibernacji w aplikacji rozruchu wiosennego
Prabu M

Cześć autor, dziękuję za fajne tutoriale i świetne posty. Stwierdziłem jednak @JsonManagedReference, @JsonBackReferenceże nie daje ci danych powiązanych @OneToManyi @ManyToOnescenariusza, również przy użyciu @JsonIgnorePropertiespomija powiązane dane encji. Jak to rozwiązać?
Pra_A,

5

Dla mnie najlepszym rozwiązaniem jest użycie @JsonViewi stworzenie określonych filtrów dla każdego scenariusza. Można także użyć @JsonManagedReferencea @JsonBackReferencejednak jest to ustalony rozwiązanie tylko jednej sytuacji, gdzie właściciel zawsze odwołuje się do posiadającą bok, nigdy odwrotnie. Jeśli masz inny scenariusz serializacji, w którym musisz zmienić adnotację w inny sposób, nie będziesz w stanie tego zrobić.

Problem

Użyjmy dwóch klas Companyi Employeetam, gdzie istnieje między nimi cykliczna zależność:

public class Company {

    private Employee employee;

    public Company(Employee employee) {
        this.employee = employee;
    }

    public Employee getEmployee() {
        return employee;
    }
}

public class Employee {

    private Company company;

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }
}

A klasa testowa, która próbuje serializować przy użyciu ObjectMapper( Spring Boot ):

@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
public class CompanyTest {

    @Autowired
    public ObjectMapper mapper;

    @Test
    public void shouldSaveCompany() throws JsonProcessingException {
        Employee employee = new Employee();
        Company company = new Company(employee);
        employee.setCompany(company);

        String jsonCompany = mapper.writeValueAsString(company);
        System.out.println(jsonCompany);
        assertTrue(true);
    }
}

Jeśli uruchomisz ten kod, otrzymasz:

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

Rozwiązanie za pomocą `@ JsonView`

@JsonViewumożliwia korzystanie z filtrów i wybieranie pól, które mają być uwzględnione podczas serializacji obiektów. Filtr to po prostu odwołanie do klasy używane jako identyfikator. Stwórzmy najpierw filtry:

public class Filter {

    public static interface EmployeeData {};

    public static interface CompanyData extends EmployeeData {};

} 

Pamiętaj, że filtry to atrapy klasy, używane tylko do określania pól z @JsonViewadnotacją, dzięki czemu możesz utworzyć tyle, ile chcesz i potrzebujesz. Zobaczmy to w akcji, ale najpierw musimy opisać naszą Companyklasę:

public class Company {

    @JsonView(Filter.CompanyData.class)
    private Employee employee;

    public Company(Employee employee) {
        this.employee = employee;
    }

    public Employee getEmployee() {
        return employee;
    }
}

i zmień Test, aby serializator mógł korzystać z widoku:

@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
public class CompanyTest {

    @Autowired
    public ObjectMapper mapper;

    @Test
    public void shouldSaveCompany() throws JsonProcessingException {
        Employee employee = new Employee();
        Company company = new Company(employee);
        employee.setCompany(company);

        ObjectWriter writter = mapper.writerWithView(Filter.CompanyData.class);
        String jsonCompany = writter.writeValueAsString(company);

        System.out.println(jsonCompany);
        assertTrue(true);
    }
}

Teraz, jeśli uruchomisz ten kod, problem nieskończonej rekurencji zostanie rozwiązany, ponieważ wyraźnie powiedziałeś, że chcesz po prostu serializować atrybuty, które zostały opatrzone adnotacjami @JsonView(Filter.CompanyData.class).

Gdy osiągnie referencję wstecz dla firmy w Employee, sprawdza, czy nie ma adnotacji i ignoruje serializację. Masz również potężne i elastyczne rozwiązanie do wyboru danych, które chcesz wysłać za pośrednictwem interfejsów API REST.

Za pomocą Springa możesz opisywać metody kontrolerów REST za pomocą pożądanego @JsonViewfiltra, a serializacja jest stosowana w sposób przezroczysty do zwracanego obiektu.

Oto importowane dane, które należy sprawdzić:

import static org.junit.Assert.assertTrue;

import javax.transaction.Transactional;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

import com.fasterxml.jackson.annotation.JsonView;

1
To miły artykuł wyjaśniający wiele alternatywnych rozwiązań rozwiązywania rekurencji: baeldung.com/…
Hugo Baés

5

@JsonIgnoreProperties jest odpowiedzią.

Użyj czegoś takiego:

@OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
@JsonIgnoreProperties("course")
private Set<Student> students;

Używaj tego pewnie, jak widziałem, że Jhipster używa tego w wygenerowanym kodzie
ifelse.codes,

Dziękuję za odpowiedź. Stwierdziłem jednak @JsonManagedReference, @JsonBackReferenceże nie daje ci danych powiązanych @OneToManyi @ManyToOnescenariusza, również przy użyciu @JsonIgnorePropertiespomija powiązane dane encji. Jak to rozwiązać?
Pra_A,

4

W moim przypadku wystarczyło zmienić relację z:

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

do:

@OneToMany
private List<Town> towns;

inna relacja pozostała taka, jaka była:

@ManyToOne
@JoinColumn(name = "county_id")
private County county;

2
Myślę, że lepiej jest użyć rozwiązania Kurta. Ponieważ rozwiązanie JoinColumn może kończyć się martwymi ciałami danych, do których się nie odwołuje.
flosk8

1
To właściwie jedyna rzecz, która mi pomogła. Żadne inne rozwiązania z góry nie działały. Nadal nie jestem pewien, dlaczego ...
Deniss M.

4

Pamiętaj, aby wszędzie używać com.fasterxml.jackson . Spędziłem dużo czasu, aby się tego dowiedzieć.

<properties>
  <fasterxml.jackson.version>2.9.2</fasterxml.jackson.version>
</properties>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>${fasterxml.jackson.version}</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${fasterxml.jackson.version}</version>
</dependency>

Następnie użyj @JsonManagedReferencei @JsonBackReference.

Wreszcie możesz serializować swój model do JSON:

import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(model);

4

Możesz użyć @JsonIgnore , ale to zignoruje dane json, do których można uzyskać dostęp z powodu relacji klucza obcego. Dlatego jeśli potrzebujesz danych klucza obcego (przez większość wymaganego czasu), to @JsonIgnore ci nie pomoże. W takiej sytuacji postępuj zgodnie z poniższym rozwiązaniem.

otrzymujesz nieskończoną rekurencję, ponieważ klasa BodyStat ponownie odwołuje się do obiektu Trainee

BodyStat

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

Stażysta

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

Dlatego musisz skomentować / pominąć powyższą część w stażu


1
W moim przypadku nie działa. Czy mógłbyś rzucić okiem: github.com/JavaHelper/issue-jackson-boot ?
Pra_A,

3

Spotkałem również ten sam problem. Kiedyś @JsonIdentityInfo„s ObjectIdGenerators.PropertyGenerator.classtyp generatora.

To jest moje rozwiązanie:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Trainee extends BusinessObject {
...

2
Dobra odpowiedź
LAGRIDA,

1
Odpowiedziałbym na to samo !!! Miło;)
Felipe Desiderati

3

Powinieneś używać @JsonBackReference z encją @ManyToOne i @JsonManagedReference z @onetomany zawierającym klasy encji.

@OneToMany(
            mappedBy = "queue_group",fetch = FetchType.LAZY,
            cascade = CascadeType.ALL
        )
    @JsonManagedReference
    private Set<Queue> queues;



@ManyToOne(cascade=CascadeType.ALL)
        @JoinColumn(name = "qid")
       // @JsonIgnore
        @JsonBackReference
        private Queue_group queue_group;

Jeśli wstawię adnotację @ jsonIgnore w dziecku. Nie mogłem pobrać obiektu nadrzędnego od dziecka. Gdy próbuję zabrać dziecko. dlaczego obiekt nadrzędny nie nadchodzi, jest ignorowany przez @ jsonignore. powiedz mi, jak przejść od dziecka do rodzica i od rodzica do dziecka.
Kumaresan Perumal

Nie ma potrzeby korzystania z @JsonIgnore wystarczy użyć powyższych adnotacji i Aby uzyskać objekty rodzica i dziecka za pomocą Getterów i seterów. i Jsonignore również robi to samo, ale stworzy nieskończoną rekurencję. Jeśli udostępniasz swój kod, mogę sprawdzić, dlaczego nie otrzymujesz obiektów. Ponieważ dla mnie oba nadchodzą.
Shubham,

Chciałem powiedzieć. kiedy bierzesz rodzica. Rodzic powinien przyjść z obiektem potomnym. podczas przyjmowania przedmiotu podrzędnego. Dziecko powinno przyjść z rodzicem. W tym scenariuszu nie działa. Czy mógłbyś mi pomóc?
Kumaresan Perumal

1

możesz użyć wzorca DTO stworzyć klasę TraineeDTO bez hibernacji anotacji, a także użyć mapera Jacksona do konwersji Stażysty na TraineeDTO i bingo komunikat o błędzie zniknie :)


1

Jeśli nie możesz zignorować właściwości, spróbuj zmodyfikować widoczność pola. W naszym przypadku nadal mieliśmy stary kod przesyłający jednostki z relacją, więc w moim przypadku była to poprawka:

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private Trainee trainee;

Jeśli wstawię adnotację @ jsonIgnore w dziecku. Nie mogłem pobrać obiektu nadrzędnego od dziecka. Gdy próbuję zabrać dziecko. dlaczego obiekt nadrzędny nie nadchodzi, jest ignorowany przez @ jsonignore. powiedz mi, jak przejść od dziecka do rodzica i od rodzica do dziecka.
Kumaresan Perumal

0

Miałem ten problem, ale nie chciałem używać adnotacji w moich bytach, więc rozwiązałem to, tworząc konstruktor dla mojej klasy, ten konstruktor nie może mieć odwołania do bytów, które odwołują się do tego bytu. Powiedzmy, że ten scenariusz.

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

Jeśli spróbujesz wysłać do widoku, klasa Blub Awraz z @ResponseBodynią może spowodować nieskończoną pętlę. Możesz napisać konstruktor w swojej klasie i utworzyć zapytanie w entityManagerten sposób.

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

To jest klasa z konstruktorem.

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

}

Istnieją jednak pewne ograniczenia dotyczące tego rozwiązania, jak widać, w konstruktorze, o którym nie wspomniałem Listy bs, ponieważ Hibernate nie pozwala na to, przynajmniej w wersji 3.6.10. Końcowe , więc kiedy potrzebuję aby pokazać oba podmioty w widoku, wykonuję następujące czynności.

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

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

Innym problemem związanym z tym rozwiązaniem jest to, że jeśli dodajesz lub usuwasz właściwość, musisz zaktualizować konstruktor i wszystkie zapytania.


0

W przypadku korzystania z Spring Data Rest problem można rozwiązać, tworząc repozytoria dla każdej jednostki zaangażowanej w cykliczne odwołania.


0

Jestem spóźniony i to już tak długi wątek. Ale spędziłem kilka godzin, próbując to rozgryźć i chciałbym podać moją sprawę jako kolejny przykład.

Wypróbowałem zarówno rozwiązania JsonIgnore, JsonIgnoreProperties, jak i BackReference, ale o dziwo było tak, jakby ich nie odebrano.

Użyłem Lomboka i pomyślałem, że może to przeszkadza, ponieważ tworzy konstruktory i zastępuje toString (zobaczył toString w stosie stackoverflowerror).

Wreszcie nie była to wina Lomboka - użyłem automatycznego generowania jednostek JPA przez NetBeans z tabel bazy danych, nie zastanawiając się nad tym dobrze - a jedną z adnotacji dodanych do wygenerowanych klas było @XmlRootElement. Po usunięciu wszystko zaczęło działać. No cóż.


0

Chodzi o to, aby umieścić @JsonIgnore w metodzie ustawiającej w następujący sposób. w moim przypadku.

Township.java

@Access(AccessType.PROPERTY)
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name="townshipId", nullable=false ,insertable=false, updatable=false)
public List<Village> getVillages() {
    return villages;
}

@JsonIgnore
@Access(AccessType.PROPERTY)
public void setVillages(List<Village> villages) {
    this.villages = villages;
}

Village.java

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "townshipId", insertable=false, updatable=false)
Township township;

@Column(name = "townshipId", nullable=false)
Long townshipId;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.