Infinite Recursion avec Jackson JSON et Hibernate JPA

412

Lorsque j'essaie de convertir un objet JPA qui a une association bidirectionnelle en JSON, je continue

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

Tout ce que j'ai trouvé, c'est ce fil qui se résume essentiellement à recommander d'éviter les associations bidirectionnelles. Quelqu'un at-il une idée d'une solution de contournement pour ce bug de printemps?

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

Extraits de code:

Objet métier 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 ...

Objet métier 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;

Manette:

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

Mise en œuvre JPA du stagiaire 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>
Ta Sas
la source
Ajouter @Transientà Trainee.bodyStats.
phil294
En 2017, @JsonIgnorePropertiesc'est la solution la plus propre. Consultez la réponse de Zammel AlaaEddine pour plus de détails.
Utku
Comment est la faute de ce printemps ??
Nathan Hughes

Réponses:

294

Vous pouvez utiliser @JsonIgnorepour briser le cycle.

axtavt
la source
1
@Ben: En fait, je ne sais pas. Peut-être que son support n'était pas activé: wiki.fasterxml.com/JacksonJAXBAnnotations
axtavt
40
Depuis Jackson 1.6, il existe une meilleure solution: vous pouvez utiliser deux nouvelles annotations pour résoudre un problème de récursion infinie sans ignorer les getters / setters pendant la sérialisation. Voir ma réponse ci-dessous pour plus de détails.
Kurt Bourbaki
1
@axtavt Merci pour la réponse parfaite. Btw, je suis venu avec une autre solution: vous pouvez simplement éviter de créer un getter pour cette valeur, donc Spring ne pourra pas y accéder pendant la création de JSON (mais je ne pense pas que cela convienne à tous les cas, donc votre réponse est meilleure)
Semyon Danilov
11
Toutes les solutions ci-dessus semblent devoir changer les objets de domaine en ajoutant des annotations. Si je sérialise des classes tierces, je n'ai aucun moyen de les modifier. Comment puis-je éviter ce problème?
Jianwu Chen
2
cette solution ne fonctionne pas dans certaines situations. Dans la base de données relationnelle avec jpa, si vous le mettez @JsonIgnorevous annulerez la "clé étrangère" lorsque vous mettrez à jour l'entité ...
slim
629

JsonIgnoreProperties [mise à jour 2017]:

Vous pouvez désormais utiliser JsonIgnoreProperties pour supprimer la sérialisation des propriétés (pendant la sérialisation) ou ignorer le traitement des propriétés JSON lues (pendant la désérialisation) . Si ce n'est pas ce que vous recherchez, veuillez continuer à lire ci-dessous.

(Merci à As Zammel AlaaEddine de l'avoir signalé).


JsonManagedReference et JsonBackReference

Depuis Jackson 1.6, vous pouvez utiliser deux annotations pour résoudre le problème de récursion infinie sans ignorer les getters / setters pendant la sérialisation: @JsonManagedReferenceet @JsonBackReference.

Explication

Pour que Jackson fonctionne correctement, l'un des deux côtés de la relation ne doit pas être sérialisé, afin d'éviter la boucle infite qui provoque votre erreur de stackoverflow.

Ainsi, Jackson prend la partie avant de la référence (votre cours Set<BodyStat> bodyStatsen classe stagiaire) et la convertit dans un format de stockage de type json; c'est ce que l'on appelle le processus de triage . Ensuite, Jackson recherche la partie arrière de la référence (c'est- Trainee traineeà- dire dans la classe BodyStat) et la laisse telle quelle, sans la sérialiser. Cette partie de la relation sera reconstruite lors de la désérialisation ( dé-marshalling ) de la référence directe.

Vous pouvez changer votre code comme ceci (je saute les parties inutiles):

Objet métier 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;

Objet métier 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;

Maintenant, tout devrait fonctionner correctement.

Si vous voulez plus d'informations, j'ai écrit un article sur les problèmes de Json et Jackson Stackoverflow sur Keenformatics , mon blog.

ÉDITER:

Une autre annotation utile que vous pouvez vérifier est @JsonIdentityInfo : en l'utilisant, chaque fois que Jackson sérialise votre objet, il lui ajoutera un ID (ou un autre attribut de votre choix), de sorte qu'il ne le "scannera" pas à chaque fois. Cela peut être utile lorsque vous avez une boucle de chaîne entre des objets plus interdépendants (par exemple: Order -> OrderLine -> User -> Order et encore).

Dans ce cas, vous devez être prudent, car vous pourriez avoir besoin de lire les attributs de votre objet plusieurs fois (par exemple dans une liste de produits avec plus de produits qui partagent le même vendeur), et cette annotation vous empêche de le faire. Je suggère de toujours regarder les journaux de Firebug pour vérifier la réponse Json et voir ce qui se passe dans votre code.

Sources:

Kurt Bourbaki
la source
29
Merci pour la réponse claire. C'est une solution plus pratique que de remettre la @JsonIgnoreréférence.
Utku Özdemir
3
C'est certainement la bonne façon de procéder. Si vous le faites comme ça du côté serveur parce que vous utilisez Jackson là-bas, peu importe le mappeur json que vous utilisez du côté client et vous n'avez pas besoin de définir l'enfant comme manuel du lien parent. Ça marche juste. Merci Kurt
flosk8
1
Belle explication détaillée et approche nettement meilleure et plus descriptive que @JsonIgnore.
Piotr Nowicki
2
Merci! @JsonIdentityInfo a fonctionné pour les références cycliques qui impliquaient plusieurs entités dans de nombreuses boucles qui se chevauchaient.
n00b
1
Je ne peux pas faire en sorte que cela fonctionne pour ma vie. Je pense que j'ai une configuration assez similaire, mais j'ai évidemment quelque chose qui ne va pas puisque je ne peux obtenir que des erreurs de recurision infinies:
swv
103

La nouvelle annotation @JsonIgnoreProperties résout de nombreux problèmes avec les autres options.

@Entity

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

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

Vérifiez le ici. Cela fonctionne exactement comme dans la documentation:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html

tero17
la source
@tero - Avec cette approche également, nous n'obtenons pas de données associées à l'entité.
Pra_A
@PAA HEY PAA je pense que c'est associé à l'entité! pourquoi tu dis ça ?
tero17
1
@ tero17 comment gérez-vous la récursion infinie lorsque vous avez plus de 2 classes? Par exemple: Classe A -> Classe B -> Classe C -> Classe A. J'ai essayé avec JsonIgnoreProperties sans chance
Villat
@Villat, c'est un autre problème à résoudre, je suggère d'ouvrir une nouvelle demande pour cela.
tero17
+1 pour l'exemple de code, en tant que débutant à Jackson, l'utilisation de @JsonIgnoreProperties n'était pas entièrement claire en lisant le JavaDoc
Wecherowski
47

Vous pouvez également utiliser Jackson 2.0+ @JsonIdentityInfo. Cela a fonctionné beaucoup mieux pour mes cours d'hibernation que @JsonBackReferenceet@JsonManagedReference , ce qui a eu des problèmes pour moi et n'a pas résolu le problème. Ajoutez simplement quelque chose comme:

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

et ça devrait marcher.

Marcus
la source
Pouvez-vous expliquer "Cela a fonctionné beaucoup mieux"? Y a-t-il un problème avec la référence gérée?
Utku Özdemir
@ UtkuÖzdemir J'ai ajouté des détails sur @JsonIdentityInfodans ma réponse ci-dessus.
Kurt Bourbaki
1
c'est la meilleure solution que nous ayons trouvée jusqu'à présent, car lorsque nous avons utilisé "@JsonManagedReference", la méthode get a correctement renvoyé les valeurs sans aucune erreur de débordement de pile. Mais, lorsque nous avons essayé d'enregistrer des données à l'aide de la publication, il a renvoyé une erreur de 415 (erreur de support non prise en charge)
cuser
1
J'ai ajouté des @JsonIdentityInfoannotations à mes entités mais cela ne résout pas le problème de récursivité. Seulement @JsonBackReferenceet @JsonManagedReferencerésout, mais ils suppriment les propriétés mappées de JSON.
Oleg Abrazhaev
19

En outre, Jackson 1.6 prend en charge la gestion des références bidirectionnelles ... ce qui semble être ce que vous recherchez ( cette entrée de blog mentionne également la fonctionnalité)

Et depuis juillet 2011, il existe également " jackson-module-hibernate " qui pourrait aider dans certains aspects de la gestion des objets Hibernate, mais pas nécessairement celui-ci (qui nécessite des annotations).

StaxMan
la source
Les liens sont morts, ça vous dérange de les mettre à jour ou de modifier votre réponse.
GetMeARemoteJob
9

Cela a parfaitement fonctionné pour moi. Ajoutez l'annotation @JsonIgnore sur la classe enfant où vous mentionnez la référence à la classe parent.

@ManyToOne
@JoinColumn(name = "ID", nullable = false, updatable = false)
@JsonIgnore
private Member member;
Manjunath BR
la source
2
Je pense @JsonIgnorequ'ignore cet attribut d'être récupéré côté client. Et si j'ai besoin de cet attribut avec son enfant (s'il a un enfant)?
Khasan 24-7
6

Il existe maintenant un module Jackson (pour Jackson 2) spécialement conçu pour gérer les problèmes d'initialisation paresseuse d'Hibernate lors de la sérialisation.

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

Ajoutez simplement la dépendance (notez qu'il existe différentes dépendances pour Hibernate 3 et Hibernate 4):

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

puis enregistrez le module lors de l'initialisation de l'ObjectMapper de Jackson:

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

La documentation n'est actuellement pas géniale. Voir le code Hibernate4Module pour les options disponibles.

Shane
la source
Le problème est alors de le résoudre, car il semble intéressant. J'ai le même problème que l'OP et toutes les astuces, y compris ci-dessus, n'ont pas fonctionné.
user1567291
6

Fonctionne bien pour moi Résoudre le problème de récursion Json Infinite lorsque vous travaillez avec Jackson

C'est ce que j'ai fait dans oneToMany et ManyToOne Mapping

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


@OneToMany(mappedBy="LgcyIsp ")
@JsonManagedReference
private List<Safety> safety;
Prabu M
la source
J'ai utilisé la cartographie d'hibernation dans l'application Spring Boot
Prabu M
Salut auteur, merci pour les tutoriels sympas et les bons articles. Cependant, j'ai trouvé que @JsonManagedReference, @JsonBackReferencene vous donne pas les données associées à @OneToManyet le @ManyToOnescénario, également lors de l'utilisation ignore les données d' @JsonIgnorePropertiesentité associées. Comment résoudre ça?
Pra_A
5

Pour moi, la meilleure solution est d'utiliser @JsonViewet de créer des filtres spécifiques pour chaque scénario. Vous pouvez également utiliser @JsonManagedReferenceet@JsonBackReference , cependant, c'est une solution codée en dur à une seule situation, où le propriétaire fait toujours référence au côté propriétaire, et jamais l'inverse. Si vous avez un autre scénario de sérialisation où vous devez ré-annoter l'attribut différemment, vous ne pourrez pas.

Problème

Permet d'utiliser deux classes, Companyet Employeeoù vous avez une dépendance cyclique entre elles:

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

Et la classe de test qui essaie de sérialiser en utilisant 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);
    }
}

Si vous exécutez ce code, vous obtiendrez:

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

Solution utilisant `@ JsonView`

@JsonViewvous permet d'utiliser des filtres et de choisir les champs à inclure lors de la sérialisation des objets. Un filtre n'est qu'une référence de classe utilisée comme identifiant. Créons donc d'abord les filtres:

public class Filter {

    public static interface EmployeeData {};

    public static interface CompanyData extends EmployeeData {};

} 

N'oubliez pas que les filtres sont des classes factices, juste utilisés pour spécifier les champs avec l' @JsonViewannotation, vous pouvez donc en créer autant que vous le souhaitez et en avez besoin. Voyons cela en action, mais nous devons d'abord annoter notre Companyclasse:

public class Company {

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

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

    public Employee getEmployee() {
        return employee;
    }
}

et modifiez le test pour que le sérialiseur utilise la vue:

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

Maintenant, si vous exécutez ce code, le problème de récursion infinie est résolu, car vous avez explicitement dit que vous souhaitez simplement sérialiser les attributs qui ont été annotés avec @JsonView(Filter.CompanyData.class) .

Lorsqu'il atteint la référence arrière de la société dans le Employee, il vérifie qu'il n'est pas annoté et ignore la sérialisation. Vous disposez également d'une solution puissante et flexible pour choisir les données que vous souhaitez envoyer via vos API REST.

Avec Spring, vous pouvez annoter vos méthodes de contrôleurs REST avec le @JsonViewfiltre souhaité et la sérialisation est appliquée de manière transparente à l'objet renvoyé.

Voici les importations utilisées au cas où vous auriez besoin de vérifier:

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;
fabioresner
la source
1
Ceci est un bel article expliquant de nombreuses solutions alternatives pour résoudre les récursions: baeldung.com/…
Hugo Baés
5

@JsonIgnoreProperties est la réponse.

Utilisez quelque chose comme ça ::

@OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
@JsonIgnoreProperties("course")
private Set<Student> students;
ifelse.codes
la source
Utilisez-le en toute confiance car j'ai vu que Jhipster l'utilise dans son code généré
ifelse.codes
Merci d'avoir répondu. Cependant, j'ai trouvé que @JsonManagedReference, @JsonBackReferencene vous donne pas les données associées à @OneToManyet le @ManyToOnescénario, également lors de l'utilisation ignore les données d' @JsonIgnorePropertiesentité associées. Comment résoudre ça?
Pra_A
4

Dans mon cas, il suffisait de changer la relation de:

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

à:

@OneToMany
private List<Town> towns;

une autre relation est restée telle qu'elle était:

@ManyToOne
@JoinColumn(name = "county_id")
private County county;
Klapsa2503
la source
2
Je pense qu'il vaut mieux utiliser la solution de Kurt. Parce que la solution JoinColumn peut se terminer par des cadavres de données non référencés.
flosk8
1
C'est en fait la seule chose qui m'a aidé. Aucune autre solution par le haut n'a fonctionné. Je ne sais toujours pas pourquoi ...
Deniss M.
4

Assurez-vous d'utiliser com.fasterxml.jackson partout. J'ai passé beaucoup de temps à le découvrir.

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

Ensuite, utilisez @JsonManagedReferenceet @JsonBackReference.

Enfin, vous pouvez sérialiser votre modèle en JSON:

import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(model);
Sveteek
la source
4

Vous pouvez utiliser @JsonIgnore , mais cela ignorera les données json accessibles en raison de la relation de clé étrangère. Par conséquent, si vous demandez les données de clé étrangère (la plupart du temps, nous avons besoin), @JsonIgnore ne vous aidera pas. Dans une telle situation, veuillez suivre la solution ci-dessous.

vous obtenez une récursion infinie, car la classe BodyStat fait à nouveau référence à l' objet Trainee

BodyStat

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

Stagiaire

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

Par conséquent, vous devez commenter / omettre la partie ci-dessus dans le stagiaire

RAJ KASHWAN
la source
1
Dans mon cas, ça ne marche pas. Pourriez-vous s'il vous plaît jeter un oeil: github.com/JavaHelper/issue-jackson-boot ?
Pra_A
3

J'ai également rencontré le même problème. J'ai utilisé @JsonIdentityInfole ObjectIdGenerators.PropertyGenerator.classtype de générateur.

Voilà ma solution:

@Entity
@Table(name = "ta_trainee", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Trainee extends BusinessObject {
...
Arif Acar
la source
2
La bonne réponse
LAGRIDA
1
Je répondrais la même chose !!! Nice;)
Felipe Desiderati
3

Vous devez utiliser @JsonBackReference avec @ManyToOne entity et @JsonManagedReference avec @onetomany contenant des classes d'entités.

@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;
Shubham
la source
Si je mets l'annotation @ jsonIgnore chez l'enfant. Je n'ai pas pu obtenir d'objet parent de l'enfant lorsque j'essaie de prendre l'enfant. pourquoi l'objet parent ne vient pas, il est ignoré par @ jsonignore. dites-moi comment passer d'un enfant à un parent et d'un parent à un enfant.
Kumaresan Perumal
Pas besoin d'utiliser @JsonIgnore, utilisez simplement les annotations ci-dessus et pour obtenir des objets du parent et de l'enfant en utilisant des Getters et des setters. et Jsonignore fait aussi la même chose mais cela créera une récursion infinie. Si vous partagez votre code, je peux vérifier pourquoi vous n'obtenez pas d'objets. Parce que pour moi les deux arrivent.
Shubham
Je voulais dire. lors de la prise d'un parent. Le parent doit venir avec un objet enfant. lors de la prise d'un objet enfant. L'enfant doit venir avec un parent. Il ne fonctionne pas dans ce scénario. pourrais-tu m'aider s'il te plaît?
Kumaresan Perumal
1

vous pouvez utiliser le modèle DTO créer la classe TraineeDTO sans aucune annotation hiberbnate et vous pouvez utiliser jackson mapper pour convertir Trainee en TraineeDTO et bingo le message d'erreur disparaît :)

Dahar Youssef
la source
1

Si vous ne pouvez pas ignorer la propriété, essayez de modifier la visibilité du champ. Dans notre cas, nous avions encore un ancien code soumettant des entités avec la relation, donc dans mon cas, c'était le correctif:

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private Trainee trainee;
Scott Langeberg
la source
Si je mets l'annotation @ jsonIgnore chez l'enfant. Je n'ai pas pu obtenir d'objet parent de l'enfant lorsque j'essaie de prendre l'enfant. pourquoi l'objet parent ne vient pas, il est ignoré par @ jsonignore. dites-moi comment passer d'un enfant à un parent et d'un parent à un enfant.
Kumaresan Perumal
0

J'ai eu ce problème, mais je ne voulais pas utiliser d'annotation dans mes entités, j'ai donc résolu en créant un constructeur pour ma classe, ce constructeur ne doit pas avoir de référence aux entités qui référencent cette entité. Disons ce scénario.

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

Si vous essayez d'envoyer à la vue la classe Bou Aavec @ResponseBodyelle peut provoquer une boucle infinie. Vous pouvez écrire un constructeur dans votre classe et créer une requête avec votre entityManagercomme ceci.

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

Ceci est la classe avec le constructeur.

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

}

Cependant, comme vous pouvez le constater, dans le constructeur, je n'ai pas fait référence à List bs, car Hibernate ne le permet pas, du moins dans la version 3.6.10 . Enfin , donc quand j'ai besoin pour montrer les deux entités dans une vue, je fais ce qui suit.

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

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

L'autre problème avec cette solution est que si vous ajoutez ou supprimez une propriété, vous devez mettre à jour votre constructeur et toutes vos requêtes.

OJVM
la source
0

Si vous utilisez Spring Data Rest, le problème peut être résolu en créant des référentiels pour chaque entité impliquée dans les références cycliques.

Morik
la source
0

Je suis un retardataire et c'est déjà un fil si long. Mais j'ai passé quelques heures à essayer de comprendre cela aussi, et je voudrais donner mon cas comme un autre exemple.

J'ai essayé les solutions JsonIgnore, JsonIgnoreProperties et BackReference, mais étrangement, c'était comme si elles n'avaient pas été récupérées.

J'ai utilisé Lombok et j'ai pensé que cela interférait peut-être, car il crée des constructeurs et remplace toString (scie toString dans la pile stackoverflowerror).

Enfin, ce n'était pas la faute de Lombok - j'ai utilisé la génération automatique NetBeans d'entités JPA à partir de tables de base de données, sans trop y réfléchir - et l'une des annotations qui ont été ajoutées aux classes générées était @XmlRootElement. Une fois que je l'ai retiré, tout a commencé à fonctionner. Tant pis.

bonjour_earth
la source
0

Le point est de placer le @JsonIgnore dans la méthode setter comme suit. dans mon cas.

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;
zawhtut
la source