答案 0 :(得分:537)
您现在可以使用JsonIgnoreProperties来禁止属性的序列化(在序列化期间),或忽略处理读取的JSON属性(在反序列化期间)。如果这不是您想要的,请继续阅读下面的内容。
(感谢As Zammel AlaaEddine指出这一点)。
从Jackson 1.6开始,您可以使用两个注释来解决无限递归问题,而不会在序列化期间忽略getter / setter:@JsonManagedReference
和@JsonBackReference
。
<强>解释强>
为了让Jackson工作得很好,关系的两个方面之一不应该被序列化,以避免导致你的stackoverflow错误的infite循环。
因此,杰克逊采用了引用的前向部分(你在训练班中的Set<BodyStat> bodyStats
),并以类似json的存储格式转换它;这就是所谓的编组过程。然后,Jackson查找引用的后半部分(即BodyStat类中的Trainee trainee
)并保持原样,而不是序列化它。在前向引用的反序列化(解组)期间,将重新构建此部分关系。
您可以像这样更改代码(我跳过无用的部分):
业务对象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;
业务对象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;
现在一切都应该正常运作。
如果你想了解更多信息,我写了一篇关于Json and Jackson Stackoverflow issues on Keenformatics的文章,我的博客。
编辑:
你可以检查的另一个有用的注释是@JsonIdentityInfo:使用它,每当Jackson序列化你的对象时,它会向它添加一个ID(或你选择的另一个属性),这样它就不会完全“扫描” “每次都这样。当你在更多相互关联的对象之间有一个链循环时(例如:Order - &gt; OrderLine - &gt; User - &gt; Order and over again),这可能很有用。
在这种情况下,您必须要小心,因为您可能需要多次读取对象的属性(例如,在产品列表中包含更多共享同一卖家的产品),并且此注释会阻止您执行此操作所以。我建议总是看看firebug日志来检查Json响应,看看你的代码中发生了什么。
<强>来源:强>
答案 1 :(得分:243)
答案 2 :(得分:83)
新注释@JsonIgnoreProperties解决了其他选项的许多问题。
@Entity
public class Material{
...
@JsonIgnoreProperties("costMaterials")
private List<Supplier> costSuppliers = new ArrayList<>();
...
}
@Entity
public class Supplier{
...
@JsonIgnoreProperties("costSuppliers")
private List<Material> costMaterials = new ArrayList<>();
....
}
在这里查看。它就像在文档中一样工作:
http://springquay.blogspot.com/2016/01/new-approach-to-solve-json-recursive.html
答案 3 :(得分:44)
此外,使用Jackson 2.0+,您可以使用@JsonIdentityInfo
。对于我的hibernate类而言,这比@JsonBackReference
和@JsonManagedReference
更好,这对我来说有问题并且没有解决问题。只需添加以下内容:
@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 {
它应该有用。
答案 4 :(得分:19)
此外,杰克逊1.6支持handling bi-directional references ...似乎 你在寻找什么(this blog entry也提到了这个特征)
截至2011年7月,还有“jackson-module-hibernate”在处理Hibernate对象的某些方面可能有所帮助,尽管不一定是特定的(需要注释)。
答案 5 :(得分:11)
现在Jackson支持避免循环而不忽略字段:
Jackson - serialization of entities with birectional relationships (avoiding cycles)
答案 6 :(得分:8)
这对我来说非常好。 在子类中添加注释@JsonIgnore,其中提到对父类的引用。
@ManyToOne
@JoinColumn(name = "ID", nullable = false, updatable = false)
@JsonIgnore
private Member member;
答案 7 :(得分:6)
现在有一个Jackson模块(用于Jackson 2)专门设计用于在序列化时处理Hibernate延迟初始化问题。
https://github.com/FasterXML/jackson-datatype-hibernate
只需添加依赖项(注意Hibernate 3和Hibernate 4有不同的依赖项):
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.4.0</version>
</dependency>
然后在初始化Jackson的ObjectMapper时注册模块:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate4Module());
目前文档不是很好。有关可用选项,请参阅Hibernate4Module code。
答案 8 :(得分:5)
对我来说,最好的解决方案是使用@JsonView
并为每个方案创建特定的过滤器。您也可以使用@JsonManagedReference
和@JsonBackReference
,但它只是一种硬编码解决方案,只有一种情况,所有者总是引用拥有方,而不是相反。如果您有另一个序列化方案需要以不同方式重新标注属性,则无法进行。
让我们使用两个类Company
和Employee
,它们之间存在循环依赖关系:
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;
}
}
尝试使用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);
}
}
如果您运行此代码,您将获得:
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
@JsonView
使您可以使用过滤器并选择在序列化对象时应包含哪些字段。过滤器只是用作标识符的类引用。所以我们首先创建过滤器:
public class Filter {
public static interface EmployeeData {};
public static interface CompanyData extends EmployeeData {};
}
请记住,过滤器是虚拟类,仅用于指定带有@JsonView
注释的字段,因此您可以根据需要创建任意数量。让我们看看它的实际效果,但首先我们需要注释我们的Company
类:
public class Company {
@JsonView(Filter.CompanyData.class)
private Employee employee;
public Company(Employee employee) {
this.employee = employee;
}
public Employee getEmployee() {
return employee;
}
}
并更改测试以使序列化程序使用View:
@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);
}
}
现在,如果您运行此代码,则无限递归问题已解决,因为您已明确表示您只想序列化使用@JsonView(Filter.CompanyData.class)
注释的属性。
当它到达Employee
中的公司的后向引用时,它会检查它是否未注释并忽略序列化。您还可以使用功能强大且灵活的解决方案来选择要通过REST API发送的数据。
使用Spring,您可以使用所需的@JsonView
过滤器注释REST控制器方法,并且序列化将透明地应用于返回的对象。
以下是您需要检查时使用的导入:
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;
答案 9 :(得分:4)
就我而言,改变之间的关系就足够了:
@OneToMany(mappedBy = "county")
private List<Town> towns;
为:
@OneToMany
private List<Town> towns;
另一种关系保持不变:
@ManyToOne
@JoinColumn(name = "county_id")
private County county;
答案 10 :(得分:4)
对我来说很好 Resolve Json Infinite Recursion problem when working with Jackson
这是我在oneToMany和ManyToOne Mapping
中所做的@ManyToOne
@JoinColumn(name="Key")
@JsonBackReference
private LgcyIsp Key;
@OneToMany(mappedBy="LgcyIsp ")
@JsonManagedReference
private List<Safety> safety;
答案 11 :(得分:4)
@JsonIgnoreProperties 就是答案。
使用类似这样的东西::
{{1}}
答案 12 :(得分:4)
请务必在任何地方使用 com.fasterxml.jackson 。我花了很多时间才找到它。
<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>
然后使用@JsonManagedReference
和@JsonBackReference
。
最后,您可以将模型序列化为JSON:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(model);
答案 13 :(得分:2)
您应将@JsonBackReference与@ManyToOne实体一起使用,并将@JsonManagedReference与@onetomany包含实体类一起使用。
@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;
答案 14 :(得分:2)
您可以使用 @JsonIgnore ,但这会忽略因外键关系而可以访问的json数据。因此,如果您重新检查外键数据(我们需要的大部分时间),那么 @JsonIgnore 将无法帮助您。在这种情况下,请遵循以下解决方案。
您正在获得无限递归,因为 BodyStat 类再次引用实习生对象
<强> BodyStat 强>
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinColumn(name="trainee_fk")
private Trainee trainee;
<强>生强>
@OneToMany(mappedBy = "trainee", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Column(nullable = true)
private Set<BodyStat> bodyStats;
因此,您必须在实习生
中评论/省略上述部分答案 15 :(得分:2)
如果您使用 @JsonManagedReference
、@JsonBackReference
或 @JsonIgnore
注释,它会忽略某些字段并使用 Jackson JSON 解决无限递归。
但是如果您使用 @JsonIdentityInfo
也避免了无限递归并且您可以获得所有字段值,那么我建议您使用 @JsonIdentityInfo
注释。
@JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator.class, property="@id")
请参阅这篇文章 https://www.toptal.com/javascript/bidirectional-relationship-in-json 以更好地了解 @JsonIdentityInfo
注释。
答案 16 :(得分:1)
出于某种原因,就我而言,它不适用于Set。我不得不将其更改为List并使用@JsonIgnore和@ ToString.Exclude使其正常工作。
用列表替换集:
//before
@OneToMany(mappedBy="client")
private Set<address> addressess;
//after
@OneToMany(mappedBy="client")
private List<address> addressess;
并添加@JsonIgnore和@ ToString.Exclude注释:
@ManyToOne
@JoinColumn(name="client_id", nullable = false)
@JsonIgnore
@ToString.Exclude
private Client client;
答案 17 :(得分:1)
非常重要::如果您使用的是LOMBOK,请确保排除集合的属性,例如Set,List等...
赞:
@EqualsAndHashCode(exclude = {"attributeOfTypeList", "attributeOfTypeSet"})
答案 18 :(得分:1)
我也遇到了同样的问题。我使用了@JsonIdentityInfo
的{{1}}生成器类型。
这是我的解决方案:
ObjectIdGenerators.PropertyGenerator.class
答案 19 :(得分:1)
你可以使用DTO模式 在没有任何anotation hiberbnate的情况下创建类TraineeDTO,你可以使用jackson mapper将Trainee转换为TraineeDTO和bingo错误信息disapeare:)
答案 20 :(得分:0)
如果您不能忽略该属性,请尝试修改该字段的可见性。在我们的案例中,我们仍然使用旧代码提交具有该关系的实体,因此在我的案例中,这就是解决方法:
scss
答案 21 :(得分:0)
我来晚了,已经很长了。但是我也花了几个小时试图弄清楚这一点,并且想举另一个例子。
我尝试了JsonIgnore,JsonIgnoreProperties和BackReference解决方案,但奇怪的是,好像它们没有被使用。
我使用Lombok并认为它可能会干扰,因为它创建了构造函数并覆盖了toString(在stackoverflowerror堆栈中看到了toString)。
最后,这不是Lombok的错-我使用了从数据库表自动生成NetBeans的JPA实体的方式,没有多加考虑-很好,添加到生成的类中的注释之一是@XmlRootElement。一旦删除它,一切便开始工作。哦,很好。
答案 22 :(得分:0)
重点是将 @JsonIgnore 放置在setter方法中,如下所示。就我而言。
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;
答案 23 :(得分:0)
如果您使用的是Spring Data Rest,可以通过为周期性引用中涉及的每个实体创建存储库来解决问题。
答案 24 :(得分:0)
经过更多分析后,我遇到了同样的问题,我知道,我们也可以通过将 @JsonBackReference 保留在OneToMany批注中来获取映射实体
@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)
@JsonBackReference
private Set<BodyStat> bodyStats;
答案 25 :(得分:0)
我有这个问题,但我不想在我的实体中使用注释,所以我通过为我的类创建一个构造函数来解决,这个构造函数必须没有引用回引用这个实体的实体。我们来说这个场景吧。
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;
}
如果您尝试使用B
向视图发送课程A
或@ResponseBody
,则可能会导致无限循环。您可以在班级中编写构造函数,并使用entityManager
创建一个查询。
"select new A(id, code, name) from A"
这是带有构造函数的类。
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;
}
}
然而,有一些关于这个解决方案的限制,正如你所看到的,在构造函数中我没有引用 List bs 这是因为Hibernate不允许它,至少在< strong>版本3.6.10.Final ,所以当我需要在视图中显示两个实体时,我会执行以下操作。
public A getAById(int id); //THE A id
public List<B> getBsByAId(int idA); //the A id.
此解决方案的另一个问题是,如果添加或删除属性,则必须更新构造函数和所有查询。
答案 26 :(得分:0)
我也遇到过同样的问题,添加jsonbackref和jsonmanagedref,请确保@override等于和hashCode方法,这肯定可以解决此问题。
答案 27 :(得分:0)
这篇文章:https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion 有完整的解释。
如果你使用老版本的 Jackson,你可以试试@jsonmanagedreference + @jsonbackreference。如果您的 Jackson 高于 2(据我所知,1.9 也不起作用),请尝试使用 @JsonIdentityInfo。