Ricorsione infinita con Jackson JSON e Hibernate JPA


412

Quando provo a convertire un oggetto JPA che ha un'associazione bidirezionale in JSON, continuo a ottenere

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

Tutto quello che ho trovato è questa discussione che sostanzialmente si conclude con la raccomandazione di evitare le associazioni bidirezionali. Qualcuno ha un'idea per una soluzione alternativa per questo bug di primavera?

------ MODIFICA 24/07/2010 16:26:22 -------

Frammenti di codice:

Oggetto aziendale 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 ...

Oggetto aziendale 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;

controller:

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

Implementazione dell'APP del tirocinante 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>

Aggiungi @Transienta Trainee.bodyStats.
phil294,

A partire dal 2017, @JsonIgnorePropertiesè la soluzione più pulita. Controlla la risposta di Zammel AlaaEddine per maggiori dettagli.
Utku,

Come è colpa di questa primavera ??
Nathan Hughes,

Risposte:


294

È possibile utilizzare @JsonIgnoreper interrompere il ciclo.


1
@Ben: in realtà non lo so. Forse il suo supporto non è stato abilitato: wiki.fasterxml.com/JacksonJAXBAnnotations
axtavt

40
Da Jackson 1.6 esiste una soluzione migliore: è possibile utilizzare due nuove annotazioni per risolvere il problema della ricorsione infinita senza ignorare i getter / setter durante la serializzazione. Vedi la mia risposta qui sotto per i dettagli.
Kurt Bourbaki,

1
@axtavt Grazie per la risposta perfetta. A proposito, sono arrivato con un'altra soluzione: puoi semplicemente evitare di creare getter per questo valore, quindi Spring non sarà in grado di accedervi durante la creazione di JSON (ma non penso che si adatti a tutti i casi, quindi la tua risposta è migliore)
Semyon Danilov,

11
Tutte le soluzioni sopra sembrano aver bisogno di cambiare gli oggetti del dominio aggiungendo annotazioni. Se sto serializzando classi di terze parti che non ho modo di modificarle. Come posso evitare questo problema?
Jianwu Chen,

2
questa soluzione non funziona in alcune situazioni. Nel database relazionale con jpa, se si inserisce, @JsonIgnoresi annulla in "chiave esterna" quando si aggiorna l'entità ...
slim

629

JsonIgnoreProperties [aggiornamento 2017]:

Ora è possibile utilizzare JsonIgnoreProperties per eliminare la serializzazione delle proprietà (durante la serializzazione) o ignorare l'elaborazione delle proprietà JSON lette (durante la deserializzazione) . Se questo non è quello che stai cercando, continua a leggere di seguito.

(Grazie a As Zammel AlaaEddine per averlo segnalato).


JsonManagedReference e JsonBackReference

Da Jackson 1.6 è possibile utilizzare due annotazioni per risolvere il problema della ricorsione infinita senza ignorare i getter / setter durante la serializzazione: @JsonManagedReferencee @JsonBackReference.

Spiegazione

Perché Jackson funzioni bene, uno dei due lati della relazione non dovrebbe essere serializzato, al fine di evitare il ciclo di infite che causa l'errore dello stackoverflow.

Quindi, Jackson prende la parte anteriore del riferimento (il tuo Set<BodyStat> bodyStatsin classe Trainee) e lo converte in un formato di archiviazione simile a json; questo è il cosiddetto processo di marshalling . Quindi, Jackson cerca la parte posteriore del riferimento (cioè Trainee traineenella classe BodyStat) e la lascia così com'è, non serializzandola. Questa parte della relazione verrà ricostruita durante la deserializzazione ( unmarshalling ) del riferimento in avanti.

Puoi modificare il tuo codice in questo modo (salto le parti inutili):

Oggetto aziendale 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;

Oggetto aziendale 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;

Ora dovrebbe funzionare tutto correttamente.

Se vuoi maggiori informazioni, ho scritto un articolo sui problemi di Stackoverflow di Json e Jackson su Keenformatics , il mio blog.

MODIFICARE:

Un'altra utile annotazione che potresti controllare è @JsonIdentityInfo : usandola, ogni volta che Jackson serializza il tuo oggetto, aggiungerà un ID (o un altro attributo di tua scelta), in modo che non lo "scansionerà" di nuovo ogni volta. Questo può essere utile quando hai un anello a catena tra più oggetti correlati (ad esempio: Ordine -> OrdineLinea -> Utente -> Ordine e ancora).

In questo caso devi stare attento, poiché potresti dover leggere più volte gli attributi del tuo oggetto (ad esempio in un elenco di prodotti con più prodotti che condividono lo stesso venditore) e questa annotazione ti impedisce di farlo. Suggerisco di dare sempre un'occhiata ai log di firebug per verificare la risposta di Json e vedere cosa sta succedendo nel tuo codice.

fonti:


29
Grazie per la risposta chiara. Questa è una soluzione più conveniente rispetto a rimettere @JsonIgnoreil riferimento.
Utku Özdemir,

3
Questo è sicuramente il modo giusto per farlo. Se lo fai in questo modo sul lato server perché usi Jackson lì, non importa quale json mapper usi sul lato client e non devi impostare il figlio sul manuale del link principale. Funziona e basta. Grazie Kurt
flosk8,

1
Spiegazione piacevole e dettagliata e approccio decisamente migliore e più descrittivo di @JsonIgnore.
Piotr Nowicki,

2
Grazie! @JsonIdentityInfo ha funzionato per riferimenti ciclici che hanno coinvolto più entità in molti circuiti sovrapposti.
n00b,

1
Non riesco a farlo funzionare per la vita di me. Penso di avere una configurazione abbastanza simile, ma ovviamente ho qualcosa di sbagliato dal momento che non riesco a ottenere altro che infiniti errori di ricorsione:
swv

103

La nuova annotazione @JsonIgnoreProperties risolve molti problemi con le altre opzioni.

@Entity

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

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

Controllalo qui. Funziona proprio come nella documentazione:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html


@tero - Anche con questo approccio non otteniamo dati associati all'entità.
Pra_A

@PAA HEY PAA Penso che sia associato all'entità! perché dici così ?
tero17,

1
@ tero17 come gestisci la ricorsione infinita quando hai più di 2 classi? Ad esempio: Classe A -> Classe B -> Classe C -> Classe A. Ho provato con JsonIgnoreProperties senza fortuna
Villat

@Villat questo è un altro problema da risolvere, suggerisco di aprire una nuova domanda per questo.
tero17

+1 per l'esempio di codice, in quanto principiante di Jackson l'uso di @JsonIgnoreProperties non era del tutto chiaro leggendo JavaDoc
Wecherowski

47

Inoltre, utilizzando Jackson 2.0+ è possibile utilizzare @JsonIdentityInfo. Questo ha funzionato molto meglio per le mie lezioni in letargo rispetto a @JsonBackReferencee @JsonManagedReference, che ha avuto problemi per me e non ha risolto il problema. Aggiungi qualcosa come:

@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 {

e dovrebbe funzionare.


Puoi spiegare "Funzionava molto meglio"? C'è un problema con il riferimento gestito?
Utku Özdemir,

@ UtkuÖzdemir Ho aggiunto dettagli @JsonIdentityInfonella mia risposta sopra.
Kurt Bourbaki,

1
questa è la soluzione migliore che abbiamo trovato finora, perché quando abbiamo usato "@JsonManagedReference" il metodo get ha restituito correttamente i valori senza errori di stackoverflow. Ma, quando abbiamo provato a salvare i dati utilizzando il post, ha restituito un errore di 415 (errore multimediale non supportato)
cuser

1
Ho aggiunto @JsonIdentityInfoannotazioni alle mie entità ma non risolve il problema di ricorsione. Solo @JsonBackReferencee @JsonManagedReferencerisolve, ma vengono rimosse le proprietà mappate da JSON.
Oleg Abrazhaev,

19

Inoltre, Jackson 1.6 ha il supporto per la gestione di riferimenti bidirezionali ... che sembra quello che stai cercando ( questo articolo del blog menziona anche la funzione)

E a partire da luglio 2011, esiste anche " jackson-module-hibernate " che potrebbe aiutare in alcuni aspetti della gestione degli oggetti di Hibernate, sebbene non necessariamente questo particolare (che richiede annotazioni).


I link sono morti, ti dispiace aggiornarli o modificare la tua risposta.
GetMeARemoteJob


9

Questo ha funzionato perfettamente per me. Aggiungi l'annotazione @JsonIgnore sulla classe figlio in cui menzioni il riferimento alla classe padre.

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

2
Penso che @JsonIgnoreignori questo attributo dal recupero sul lato client. E se avessi bisogno di questo attributo con suo figlio (se ha un figlio)?
Khasan 24-7

6

Ora c'è un modulo Jackson (per Jackson 2) appositamente progettato per gestire i problemi di inizializzazione lenta di Hibernate durante la serializzazione.

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

Basta aggiungere la dipendenza (si noti che esistono diverse dipendenze per Hibernate 3 e Hibernate 4):

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

e quindi registra il modulo durante l'inizializzazione dell'ObjectMapper di Jackson:

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

La documentazione al momento non è eccezionale. Vedi il codice Hibernate4Module per le opzioni disponibili.


Quindi il problema è risolvere, perché sembra interessante. Ho lo stesso problema dell'OP e tutti i trucchi, incluso sopra, non hanno funzionato.
user1567291

6

Funzionando bene per me Risolvi il problema della ricorsione infinita Json quando lavori con Jackson

Questo è ciò che ho fatto in oneToMany e ManyToOne Mapping

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


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

Ho usato la mappatura dell'ibernazione nell'applicazione di avvio a molla
Prabu M

Ciao autore, grazie per i bei tutorial e gli ottimi post. Tuttavia ho scoperto che @JsonManagedReference, @JsonBackReferencenon ti dà i dati associati @OneToManye lo @ManyToOnescenario, anche quando si utilizza @JsonIgnorePropertiessalta i dati delle entità associate. Come risolverlo?
Pra_A

5

Per me la soluzione migliore è utilizzare @JsonViewe creare filtri specifici per ogni scenario. Puoi anche usare @JsonManagedReferencee @JsonBackReference, tuttavia, è una soluzione hardcoded per una sola situazione, in cui il proprietario fa sempre riferimento al lato proprietario e mai il contrario. Se si dispone di un altro scenario di serializzazione in cui è necessario annotare nuovamente l'attributo in modo diverso, non sarà possibile.

Problema

Consente di utilizzare due classi Companye in Employeecui esiste una dipendenza ciclica tra di loro:

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

E la classe di test che tenta di serializzare utilizzando 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);
    }
}

Se esegui questo codice, otterrai:

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

Soluzione usando `@ JsonView`

@JsonViewconsente di utilizzare i filtri e scegliere quali campi devono essere inclusi durante la serializzazione degli oggetti. Un filtro è solo un riferimento di classe utilizzato come identificatore. Quindi prima creiamo i filtri:

public class Filter {

    public static interface EmployeeData {};

    public static interface CompanyData extends EmployeeData {};

} 

Ricorda, i filtri sono classi fittizie, utilizzate solo per specificare i campi con l' @JsonViewannotazione, in modo da poterne creare quanti ne desideri e di cui hai bisogno. Vediamolo in azione, ma prima dobbiamo annotare la nostra Companyclasse:

public class Company {

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

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

    public Employee getEmployee() {
        return employee;
    }
}

e modificare il test affinché il serializzatore utilizzi la vista:

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

Ora, se si esegue questo codice, il problema della ricorsione infinita viene risolto, poiché è stato esplicitamente affermato che si desidera solo serializzare gli attributi con cui sono stati annotati @JsonView(Filter.CompanyData.class).

Quando raggiunge il riferimento posteriore per la società in Employee, verifica che non sia annotato e ignora la serializzazione. Hai anche una soluzione potente e flessibile per scegliere quali dati vuoi inviare tramite le tue API REST.

Con Spring è possibile annotare i metodi dei controller REST con il @JsonViewfiltro desiderato e la serializzazione viene applicata in modo trasparente all'oggetto restituito.

Ecco le importazioni utilizzate nel caso sia necessario verificare:

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
Questo è un bell'articolo che spiega molte soluzioni alternative per risolvere le ricorsioni: baeldung.com/…
Hugo Baés,

5

@JsonIgnoreProperties è la risposta.

Usa qualcosa del genere ::

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

Usalo con fiducia come ho visto Jhipster usa questo nel suo codice generato
ifelse.codes

Grazie per la risposta. Tuttavia ho scoperto che @JsonManagedReference, @JsonBackReferencenon ti dà i dati associati @OneToManye lo @ManyToOnescenario, anche quando si utilizza @JsonIgnorePropertiessalta i dati delle entità associate. Come risolverlo?
Pra_A

4

Nel mio caso è stato sufficiente cambiare la relazione da:

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

per:

@OneToMany
private List<Town> towns;

un'altra relazione rimase com'era:

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

2
Penso che sia meglio usare la soluzione di Kurt. Perché la soluzione JoinColumn può terminare con corpi morti di dati senza riferimento.
flosk8,

1
Questa è in realtà l'unica cosa che mi ha aiutato. Nessun'altra soluzione dall'alto ha funzionato. Non sono ancora sicuro del perché ...
Deniss M.

4

Assicurati di usare com.fasterxml.jackson ovunque. Ho trascorso molto tempo a scoprirlo.

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

Quindi utilizzare @JsonManagedReferencee @JsonBackReference.

Infine, puoi serializzare il tuo modello su JSON:

import com.fasterxml.jackson.databind.ObjectMapper;

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

4

È possibile utilizzare @JsonIgnore , ma questo ignorerà i dati json a cui è possibile accedere a causa della relazione Chiave esterna. Pertanto, se richiedi i dati della chiave esterna (il più delle volte che richiediamo), @JsonIgnore non ti aiuterà. In tale situazione, seguire la soluzione seguente.

stai ricevendo una ricorsione infinita, a causa della classe BodyStat che fa nuovamente riferimento all'oggetto Trainee

Bodystat

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

apprendista

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

Pertanto, devi commentare / omettere la parte sopra in Tirocinante


1
Nel mio caso, non funziona. Potresti dare un'occhiata: github.com/JavaHelper/issue-jackson-boot ?
Pra_A

3

Ho anche incontrato lo stesso problema. Ho usato @JsonIdentityInfoil ObjectIdGenerators.PropertyGenerator.classtipo di generatore.

Questa è la mia soluzione:

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

2
La buona risposta
LAGRIDA,

1
Risponderei alla stessa cosa !!! Bello;)
Felipe Desiderati,

3

È necessario utilizzare @JsonBackReference con l'entità @ManyToOne e @JsonManagedReference con @onetomany contenente le classi di entità.

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

Se inserisco l'annotazione @ jsonIgnore nel bambino. Non sono riuscito a ottenere l'oggetto genitore dal figlio. Quando provo a prenderlo. perché l'oggetto genitore non arriva, viene ignorato da @ jsonignore. dimmi come passare da bambino a genitore e da genitore a figlio.
Kumaresan Perumal,

Non c'è bisogno di usare @JsonIgnore basta usare le annotazioni sopra e Per ottenere oggetti di genitori e figli usando Getter e setter. e anche Jsonignore sta facendo lo stesso, ma creerà un'infinita ricorsione. Se condividi il tuo codice, posso verificare perché non stai ottenendo oggetti. Perché per me stanno arrivando entrambi.
Shubham,

Intendevo dire. quando si prende un genitore. Il genitore dovrebbe venire con l'oggetto figlio. quando si prende un oggetto figlio. Il bambino dovrebbe venire con un genitore. Non funziona in questo scenario. Per favore potete aiutarmi?
Kumaresan Perumal,

1

puoi usare il modello DTO per creare la classe TraineeDTO senza alcuna hiberbnate di anotazione e puoi usare jackson mapper per convertire il tirocinante in TraineeDTO e bingo il messaggio di errore non essere più :)


1

Se non puoi ignorare la proprietà, prova a modificare la visibilità del campo. Nel nostro caso, avevamo ancora il vecchio codice che inviava entità con la relazione, quindi nel mio caso, questa era la soluzione:

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

Se inserisco l'annotazione @ jsonIgnore nel bambino. Non sono riuscito a ottenere l'oggetto genitore dal figlio. Quando provo a prenderlo. perché l'oggetto genitore non arriva, viene ignorato da @ jsonignore. dimmi come passare da bambino a genitore e da genitore a figlio.
Kumaresan Perumal,

0

Ho avuto questo problema, ma non volevo usare l'annotazione nelle mie entità, quindi ho risolto creando un costruttore per la mia classe, questo costruttore non deve avere un riferimento alle entità che fanno riferimento a questa entità. Diciamo questo scenario.

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

Se si tenta di inviare alla vista la classe Bo Acon @ResponseBodyessa può causare un ciclo infinito. Puoi scrivere un costruttore nella tua classe e creare una query con la tua entityManagercome questa.

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

Questa è la classe con il costruttore.

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

}

Tuttavia, ci sono alcune costrizioni su questa soluzione, come puoi vedere, nel costruttore non ho fatto un riferimento all'Elenco bs, perché Hibernate non lo consente, almeno nella versione 3.6.10 . Finale , quindi quando ho bisogno per mostrare entrambe le entità in una vista, faccio quanto segue.

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

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

L'altro problema con questa soluzione è che se aggiungi o rimuovi una proprietà devi aggiornare il costruttore e tutte le tue query.


0

Nel caso in cui si utilizzi Spring Data Rest, il problema può essere risolto creando Repository per ogni Entità coinvolta in riferimenti ciclici.


0

Sono in ritardo ed è già un filo così lungo. Ma ho trascorso un paio d'ore a cercare di capire anche questo, e vorrei dare il mio caso come altro esempio.

Ho provato entrambe le soluzioni JsonIgnore, JsonIgnoreProperties e BackReference, ma stranamente è stato come se non fossero stati raccolti.

Ho usato Lombok e ho pensato che forse interferisce, dal momento che crea costruttori e sostituisce String (visto to String in stackoverflowerror stack).

Alla fine non è stata colpa di Lombok - ho usato la generazione automatica NetBeans di entità JPA dalle tabelle del database, senza pensarci troppo - bene, e una delle annotazioni aggiunte alle classi generate era @XmlRootElement. Una volta rimosso, tutto ha iniziato a funzionare. Oh bene.


0

Il punto è posizionare @JsonIgnore nel metodo setter come segue. nel mio caso.

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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.