当尝试将具有双向关联的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<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 ...
业务对象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;
}
}
jpa实习生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();
}
}
持久性.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>
23条答案
按热度按时间0vvn1miw1#
我有这个问题,但我不想在我的实体中使用注解,所以我通过为我的类创建一个构造函数来解决,这个构造函数不能引用回引用这个实体的实体。假设这个场景。
如果您试图发送到视图类
B
或者A
与@ResponseBody
它可能导致无限循环。可以在类中编写构造函数,并使用entityManager
这样地。这是具有构造函数的类。
然而,也有一些常量
bqucvtff2#
在做了更多的分析之后,我也遇到了同样的问题,我知道,我们也可以通过将@jsonbackreference保留在onetomany注解中来获得Map实体
42fyovps3#
如果无法忽略该属性,请尝试修改字段的可见性。在我们的例子中,旧代码仍然提交具有关系的实体,因此在我的例子中,这是修复方法:
rryofs0p4#
如果你使用
@JsonManagedReference
,@JsonBackReference
或者@JsonIgnore
注解它忽略一些字段,并用jackson-json解决无限递归。但如果你用
@JsonIdentityInfo
这也避免了无限递归,您可以得到所有字段的值,所以我建议您使用@JsonIdentityInfo
注解。参考本文https://www.toptal.com/javascript/bidirectional-relationship-in-json 为了更好地了解
@JsonIdentityInfo
注解。r55awzrz5#
你可以用
@JsonIgnore
打破循环(参考)。你需要导入
org.codehaus.jackson.annotate.JsonIgnore
(旧版本)或com.fasterxml.jackson.annotation.JsonIgnore
(当前版本)。s2j5cfk06#
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查找引用的后面部分(即。Trainee trainee
在bodystat类中),并保持原样,而不是序列化它。这部分关系将在前向引用的反序列化(解组)过程中重新构建。您可以这样更改代码(我跳过无用的部分):
业务对象1:
业务对象2:
现在一切都正常了。
如果你想了解更多信息,我在我的博客keenformatics上写了一篇关于json和jackson stackoverflow问题的文章。
编辑:
您可以检查的另一个有用的注解是@jsonidentityinfo:使用它,每次jackson序列化您的对象时,它都会向它添加一个id(或您选择的另一个属性),这样它就不会每次都完全“扫描”它。当您在更相关的对象(例如:order->orderline->user->order和over)之间有一个链循环时,这会很有用。
在这种情况下,您必须小心,因为您可能需要多次读取对象的属性(例如,在具有多个共享同一卖家的产品的产品列表中),并且此注解阻止您这样做。我建议经常查看firebug日志来检查json响应,看看代码中发生了什么。
资料来源:
如何解决json无限递归堆栈溢出(我的博客)
Jackson推荐信
个人经历
yk9xbfzb7#
新的注解@jsonignoreproperties解决了其他选项的许多问题。
看看这里。它的工作原理与文档中的一样:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html
62o28rlo8#
另外,使用jackson2.0+,您可以使用
@JsonIdentityInfo
. 这对我的hibernate类来说比@JsonBackReference
以及@JsonManagedReference
,这对我来说有问题,但没有解决问题。只需添加以下内容:它应该有用。
wvmv3b1j9#
另外,jackson1.6支持处理双向引用。。。这似乎是你正在寻找的(这篇博客文章也提到了这个功能)
截至2011年7月,还有“jackson module hibernate”,它可能在处理hibernate对象的某些方面有所帮助,尽管不一定是这个特定的模块(它确实需要注解)。
hrysbysz10#
现在jackson支持避免循环而不忽略字段:
jackson-具有双方向关系的实体序列化(避免循环)
s6fujrry11#
这对我来说非常好。在提到父类引用的子类上添加注解@jsonignore。
r1zk6ea112#
对我来说工作很好解决json无限递归问题时与Jackson的工作
这就是我在一个多域和多个Map中所做的
jyztefdp13#
现在有一个jackson模块(针对jackson2)专门设计用来处理序列化时的hibernate延迟初始化问题。
https://github.com/fasterxml/jackson-datatype-hibernate
只需添加依赖项(注意hibernate 3和hibernate 4有不同的依赖项):
然后在初始化jackson的objectmapper时注册模块:
文档目前不是很好。有关可用选项,请参阅hibernate4module代码。
icomxhvb14#
对我来说,最好的解决办法是
@JsonView
并为每个场景创建特定的过滤器。你也可以用@JsonManagedReference
以及@JsonBackReference
但是,它是一种只针对一种情况的硬编码解决方案,即所有者总是引用所有者的一方,而不是相反的一方。如果您有另一个序列化场景,需要以不同的方式重新注解属性,那么您将无法这样做。问题
我们用两个类,
Company
以及Employee
它们之间存在循环依赖关系:以及尝试使用
ObjectMapper
(Spring Boot):如果运行此代码,将得到:
使用
@jsonview的解决方案
@JsonView
使您能够使用筛选器并选择序列化对象时应包含哪些字段。筛选器只是用作标识符的类引用。我们先来创建过滤器:记住,过滤器是虚拟类,只是用于指定具有
@JsonView
注解,因此您可以根据需要创建任意数量的注解。让我们看看它的实际情况,但首先我们需要注解Company
班级:并更改测试以使序列化程序使用视图:
现在,如果您运行这段代码,无限递归问题就解决了,因为您明确地说您只想序列化用注解的属性
@JsonView(Filter.CompanyData.class)
.当它到达公司在
Employee
,它检查它是否未被注解并忽略序列化。您还有一个强大而灵活的解决方案来选择要通过restapi发送哪些数据。使用spring,您可以使用所需的
@JsonView
过滤器和序列化将透明地应用于返回的对象。以下是您需要检查时使用的导入:
htzpubme15#
就我而言,这足以改变这种关系:
收件人:
另一种关系保持原样: