无限递归的问题:Jackson JSON和Hibernate JPA的问题。

19 浏览
0 Comments

无限递归的问题:Jackson JSON和Hibernate JPA的问题。

当尝试将具有双向关联的JPA对象转换为JSON时,我一直收到:

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

我找到的仅有的东西是这个线程,基本上得出了建议,以避免双向关联。有人有解决这个Spring Bug的想法吗?

------ 编辑 2010-07-24 16:26:22 -------

代码片段:

业务对象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 bodyStats;
    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    private Set trainings;
    @OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @Column(nullable = true)
    private Set exerciseTypes;
    public Trainee() {
        super();
    }
    //... getters/setters ...
}

业务对象2:

import javax.persistence.*;
import java.util.Date;
@Entity
@Table(name = "ta_bodystat", uniqueConstraints = {@UniqueConstraint(columnNames = {"id"})})
public class BodyStat extends BusinessObject {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "id", nullable = false)
    private Integer id;
    @Column(name = "height", nullable = true)
    private Float height;
    @Column(name = "measuretime", nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date measureTime;
    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name="trainee_fk")
    private Trainee trainee;
}

控制器:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Controller
@RequestMapping(value = "/trainees")
public class TraineesController {
    final Logger logger = LoggerFactory.getLogger(TraineesController.class);
    private Map<Long, Trainee> trainees = new ConcurrentHashMap<Long, Trainee>();
    @Autowired
    private ITraineeDAO traineeDAO;
    /**
     * Return json repres. of all trainees
     */
    @RequestMapping(value = "/getAllTrainees", method = RequestMethod.GET)
    @ResponseBody        
    public Collection getAllTrainees() {
        Collection allTrainees = this.traineeDAO.getAll();
        this.logger.debug("A total of " + allTrainees.size() + "  trainees was read from db");
        return allTrainees;
    }    
}

实习生DAO的JPA实现:

@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

    
        false
        
            
            
            
            
        
    

admin 更改状态以发布 2023年5月23日
0
0 Comments

您可以使用@JsonIgnore打破循环(参考)。

您需要导入org.codehaus.jackson.annotate.JsonIgnore(旧版本)或com.fasterxml.jackson.annotation.JsonIgnore(当前版本)。

0
0 Comments

JsonIgnoreProperties [2017更新]:

现在可以使用JsonIgnoreProperties来抑制属性的序列化(在序列化期间),或忽略JSON属性的处理(在反序列化期间)。如果这不是你要寻找的,那么请继续往下阅读。

(感谢As Zammel AlaaEddine指出)。


JsonManagedReference和JsonBackReference

从Jackson 1.6开始,可以使用两个注释来解决无限递归问题,而不会在序列化过程中忽略getter / setter: @JsonManagedReference@JsonBackReference

说明

为了使Jackson正常工作,关系的两个方面应该有一个不序列化,从而避免因无限循环而导致堆栈溢出错误。

因此,Jackson将引用的前置部分(在Trainee类中的 Set bodyStats)转换成json格式存储;这是所谓的marshalling过程。然后,Jackson查找引用的后部分(即BodyStat类中的Trainee trainee),并将其保持不变,不进行序列化。在向前引用的反序列化(unmarshalling)期间,此关系的这一部分将被重新构建。

你可以像这样更改你的代码(跳过无用的部分):

业务对象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 bodyStats;

业务对象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;

现在应该都可以正常工作了。

如果你想要更多的信息,我在我的博客Keenformatics上写了一篇关于Json和Jackson Stackoverflow问题的文章

编辑:

你可以检查另一个有用的注解@JsonIdentityInfo:使用它,每次Jackson序列化你的对象时,它将为其添加一个ID(或你选择的另一个属性),这样它就不会每次完全“扫描”它。当你有多个相互关联的对象之间的循环链时(例如:订单->订单行->用户->订单等等),这非常有用。

在这种情况下,你必须小心,因为你可能需要多次读取你的对象的属性(例如在具有共享同一卖家的多个产品的产品列表中),而这个注解会阻止你这样做。我建议始终查看firebug日志以检查Json响应,以了解你的代码中发生了什么。

来源:

0