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

当尝试将具有双向关联的JPA对象转换为JSON时,我不断得到

org.codehaus.jackson.map.JsonMappingException:无限递归(StackOverflower错误)

我只找到了此线程的基本结论是建议避免双向关联。有没有人有办法解决这个spring bug

——编辑2010-07-2416:26:22——-

代码片段:

业务对象1:

@Entity
@表(名称=“TAU培训生”,uniqueConstraints={@UniqueConstraint(columnNames={id})})
公共类培训生扩展BusinessObject{
@身份证
@GeneratedValue(策略=GenerationType.TABLE)
@列(名称=“id”,可空=false)
私有整数id;
@列(name="name",null=true)
私有字符串名称;
@列(名称=“姓氏”,可空=真)
私家姓;
@OneToMany(mappedBy=“培训生”,fetch=FetchType.EAGER,cascade=CascadeType.ALL)
@列(nullable=true)
私人设置<BodyStat>bodyStats;
@OneToMany(mappedBy=“培训生”,fetch=FetchType.EAGER,cascade=CascadeType.ALL)
@列(nullable=true)
私人设置<培训>培训;
@OneToMany(mappedBy=“培训生”,fetch=FetchType.EAGER,cascade=CascadeType.ALL)
@列(nullable=true)
私有集<ExerciseType>exerciseTypes;
公共培训生(){
超级();
}
//…接受者/接受者。。。
}

业务对象2:

导入javax.persistence.*;
导入java.util.Date;
@实体
@表(name="tau bodystat",uniqueConstraints={@UniqueConstraint(columnNames={"id"})})
公共类BodyStat扩展了BusinessObject{
@身份证
@GeneratedValue(策略=GenerationType.TABLE)
@列(名称=“id”,可空=false)
私有整数id;
@列(名称=“高度”,可空=真)
私人浮动高度;
@列(name="measuretime",null=false)
@时态(TemporalType.TIMESTAMP)
私人日期测量时间;
@manytone(fetch=FetchType.EAGER,cascade=CascadeType.ALL)
@JoinColumn(名称=“培训生fk”)
私人见习生;
}

控制器:

import org.slf4j.Logger;
导入org.slf4j.LoggerFactory;
导入org.springframework.beans.factory.annotation.Autowired;
导入org.springframework.stereotype.Controller;
导入org.springframework.ui.Model;
导入org.springframework.web.bind.annotation.RequestBody;
导入org.springframework.web.bind.annotation.RequestMapping;
导入org.springframework.web.bind.annotation.RequestMethod;
导入org.springframework.web.bind.annotation.ResponseBody;
导入javax.servlet.http.HttpServletResponse;
导入javax.validation.ConstraintViolation;
导入java.util.*;
导入java.util.concurrent.ConcurrentHashMap;
@控制器
@请求映射(值=“学员”)
公营实习控制员{
最终记录器=LoggerFactory.getLogger(实习控制器类);
私有映射<Long,培训生>培训生=新ConcurrentHashMap<Long,培训生>();
@自动连线
私营工矿局培训生局;
/**
*返回所有学员的json代表
*/
@RequestMapping(值="/GetAllInter培训生",方法=RequestMethod.GET)
@应答器
公共集合GetAllInter培训生(){
Collection all培训生=this.traineeDAO.getAll();
this.logger.debug(“从db”中读取了总共“+all培训生.size()+”培训生”);
归还所有学员;
}    
}

JPA学员DAO的实施:

@Repository
@交易的
公共类TraineDao实施iTraineDao{
@持久上下文
私人实体管理者;
@交易的
公共实习生储蓄(实习生){
em.persist(培训生);
返回学员;
}
@事务(只读=真)
公共集合getAll(){
return(Collection)em.createQuery(“从学员t中选择t”).getResultList();
}
}

persistence.xml

<持久性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"
版本="1.0“&燃气轮机;
<持久性单元名称=“持久性单元名称”;“关系数据库管理系统”;交易类型="资源“本地”&燃气轮机;
<排除未列出的类别>假&lt/排除未列出的类别>
<房地产>
<属性名称=“属性名称”;hibernate.hbm2ddl.auto;值=”;“验证”/&燃气轮机;
<属性名称=“属性名称”;hibernate.archive.autodetection“;值=”;“类”/&燃气轮机;
<属性名称=“属性名称”;方言;值=”;org.hibernate.dialogue.mysql5innodbdialogue“/&燃气轮机;
<!--&书信电报;属性名称=“属性名称”;方言;值=”;org.hibernate.dialogue.hsqldialogue“/&燃气轮机--&燃气轮机;
&lt/房地产>
&lt/持久性单位>
&lt/持久性>

JsonIgnoreProperties[2017年更新]:

您现在可以使用JsonIgnoreProperties抑制属性序列化(在序列化期间),或忽略JSON属性读取的处理(在反序列化期间)。如果这不是你想要的,请继续阅读下面的内容

(感谢As Zammel AlaaEddine指出这一点)


JsonManagedReference和JsonBackReference

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

解释

为了使Jackson能够很好地工作,不应该序列化关系的两个方面之一,以避免导致stackoverflow错误的infite循环

因此,Jackson获取引用的前半部分(培训生类中的Set<BodyStat>bodyStats),并将其转换为类似json的存储格式;这就是所谓的编组过程。然后,Jackson查找引用的后半部分(即,培训生类中的培训生)并保持原样,而不是序列化。这部分关系将在前向引用的反序列化(解组)过程中重新构建

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

业务对象1:

@实体
@表(name=“ta_培训生”,uniqueConstraints={@UniqueConstraint(columnNames={“id”})})
公共类培训生扩展BusinessObject{
@OneToMany(mappedBy=“培训生”,fetch=FetchType.EAGER,cascade=CascadeType.ALL)
@列(nullable=true)
@JsonManagedReference
私人设置<BodyStat>bodyStats;

业务对象2:

@实体
@表(name=“ta_bodystat”,uniqueConstraints={@UniqueConstraint(columnNames={“id”})})
公共类BodyStat扩展了BusinessObject{
@manytone(fetch=FetchType.EAGER,cascade=CascadeType.ALL)
@JoinColumn(name=“培训生”
@JsonBackReference
私人见习生;

现在一切都应该正常工作了

如果你想了解更多信息,我写了一篇关于关于Keeformatics的Json和Jackson Stackoverflow问题,我的博客

编辑:

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

在这种情况下,您必须小心,因为您可能需要多次读取对象的属性(例如,在产品列表中,多个产品共享同一卖家)我建议您经常查看firebug日志,以检查Json响应,并查看代码中的情况

来源:

发表评论