556 Stimmen

Unendliche Rekursion mit Jackson JSON und Hibernate JPA Problem

Wenn ich versuche, ein JPA-Objekt mit einer bidirektionalen Assoziation in JSON zu konvertieren, erhalte ich immer

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

Alles, was ich gefunden habe, ist ce fil die im Wesentlichen mit der Empfehlung schließt, bidirektionale Assoziationen zu vermeiden. Hat jemand eine Idee für eine Umgehung dieses Frühjahrsfehlers?

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

Codesnippets:

Geschäftsobjekt 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 ...
}

Geschäftsobjekt 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;
    }    
}

JPA-Implementierung der DAO des Auszubildenden:

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

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

7voto

Shane Punkte 3910

Es gibt jetzt ein Jackson-Modul (für Jackson 2), das speziell dafür entwickelt wurde, Probleme mit der trägen Initialisierung von Hibernate beim Serialisieren zu behandeln.

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

Fügen Sie einfach die Abhängigkeit hinzu (beachten Sie, dass es für Hibernate 3 und Hibernate 4 unterschiedliche Abhängigkeiten gibt):

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

und registrieren dann das Modul bei der Initialisierung von Jacksons ObjectMapper:

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

Die Dokumentation ist derzeit nicht sehr umfangreich. Siehe die Hibernate4Module-Code für die verfügbaren Optionen.

6voto

Leandro Ferreira Punkte 335

SEHR WICHTIG: Wenn Sie LOMBOK verwenden, stellen Sie sicher, dass Sie Attribute von Sammlungen wie Set, List, etc. ausschließen.

Zum Beispiel so:

@EqualsAndHashCode(exclude = {"attributeOfTypeList", "attributeOfTypeSet"})

6voto

Shubham Punkte 559

Sie sollten @JsonBackReference mit @ManyToOne Entität und @JsonManagedReference mit @onetomany enthaltenden Entitätsklassen verwenden.

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

5voto

Arif Acar Punkte 1282

Ich bin auch auf das gleiche Problem gestoßen. Ich benutzte @JsonIdentityInfo 's ObjectIdGenerators.PropertyGenerator.class Generator-Typ.

Das ist meine Lösung:

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

5voto

Klapsa2503 Punkte 782

In meinem Fall war es genug, um die Beziehung zu ändern:

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

a:

@OneToMany
private List<Town> towns;

eine andere Beziehung blieb, wie sie war:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X