ChatGPT解决这个技术问题 Extra ChatGPT

杰克逊 JSON 和 Hibernate JPA 问题的无限递归

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

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

我发现的只是this thread,它基本上以建议避免双向关联作为结尾。有没有人知道这个春季错误的解决方法?

------ 编辑 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;
    }    
}

实习生 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();
    }
}

持久性.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>
@Transient 添加到 Trainee.bodyStats
截至 2017 年,@JsonIgnoreProperties 是最干净的解决方案。查看 Zammel AlaaEddine's answer 了解更多详情。
这个春天怎么错了??
也许会有所帮助:baeldung.com/…

K
Kevin Pullin

JsonIgnoreProperties [2017 更新]:

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

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

JsonManagedReference 和 JsonBackReference

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

解释

为了使 Jackson 正常工作,不应序列化关系的两侧之一,以避免导致您的 stackoverflow 错误的无限循环。

因此,Jackson 采用了引用的前向部分(您在 Trainee 类中的 Set<BodyStat> bodyStats),并将其转换为类似 json 的存储格式;这就是所谓的编组过程。然后,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<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 -> OrderLine -> User -> Order 并一遍又一遍),这可能很有用。

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

资料来源:

Keenformatics - 如何解决 JSON 无限递归 Stackoverflow(我的博客)

杰克逊参考资料

个人经验


感谢您的明确答复。这是一个比将 @JsonIgnore 放在反向引用上更方便的解决方案。
这绝对是正确的做法。如果您在服务器端这样做,因为您在那里使用 Jackson,那么您在客户端使用什么 json 映射器并不重要,您不必将子链接手动设置为父链接。它只是工作。谢谢库尔特
很好,详细的解释,绝对比 @JsonIgnore 更好、更具描述性的方法。
谢谢! @JsonIdentityInfo 适用于在许多重叠循环中涉及多个实体的循环引用。
我不能让它为我的生活工作。我想我有一个非常相似的设置,但我显然有问题,因为除了无限递归错误我什么都得不到:
Á
Álvaro González

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

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


我遇到了同样的问题,@JsonIgnore 解决了它。我有用 @XmlTransient 注释的方法,它应该做同样的事情(并且在使用 Jettison 时工作)。您认为您可以对 Jackson 使用 jaxb 注释,那么为什么这不起作用?
@Ben:其实我不知道。可能没有启用它的支持:wiki.fasterxml.com/JacksonJAXBAnnotations
从 Jackson 1.6 开始,有一个更好的解决方案:您可以使用两个新注释来解决无限递归问题,而无需在序列化过程中忽略 getter/setter。有关详细信息,请参阅下面的答案。
以上所有解决方案似乎都需要通过添加注释来更改域对象。如果我要序列化第三方类,我无法修改它们。我怎样才能避免这个问题?
此解决方案在某些情况下不起作用。在使用 jpa 的关系数据库中,如果您输入 @JsonIgnore,那么当您更新实体时,您将在“外键”中为空...
C
CorayThan

新注释 @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


@tero - 使用这种方法,我们也不会获得与实体关联的数据。
@PAA 嘿 PAA 我认为这与实体有关!你为什么这样讲 ?
@tero17 当你有两个以上的类时,你如何管理无限递归?例如:A 类 -> B 类 -> C 类 -> A 类。我尝试使用 JsonIgnoreProperties 没有运气
@Villat 这是另一个要解决的问题,我建议对此提出新的需求。
+1 代码示例,作为杰克逊新手,通过阅读 JavaDoc,@JsonIgnoreProperties 的使用并不完全清楚
M
Marcus

此外,使用 Jackson 2.0+,您可以使用 @JsonIdentityInfo。这对我的休眠类来说比 @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 {

它应该可以工作。


你能解释一下“这工作得更好”吗?托管引用有问题吗?
@UtkuÖzdemir 我在上面的回答中添加了有关 @JsonIdentityInfo 的详细信息。
这是迄今为止我们找到的最佳解决方案,因为当我们使用“@JsonManagedReference”时,get 方法成功返回了值,没有任何 stackoverflow 错误。但是,当我们尝试使用帖子保存数据时,它返回了 415 错误(不支持的媒体错误)
我已向我的实体添加了 @JsonIdentityInfo 注释,但它并没有解决递归问题。只有 @JsonBackReference@JsonManagedReference 解决了,但它们是从 JSON 中删除映射的属性。
J
Jonas

此外,Jackson 1.6 支持 handling bi-directional references... 这似乎是您正在寻找的(this blog entry 也提到了该功能)

截至 2011 年 7 月,还有“jackson-module-hibernate”可能有助于处理 Hibernate 对象的某些方面,尽管不一定是这个特定的对象(它确实需要注释)。


链接已失效,您介意更新它们还是编辑您的答案。
C
Community

现在杰克逊支持在不忽略字段的情况下避免循环:

Jackson - serialization of entities with birectional relationships (avoiding cycles)


M
Manjunath BR

这对我来说非常好。在您提到对父类的引用的子类上添加注释@JsonIgnore。

@ManyToOne
@JoinColumn(name = "ID", nullable = false, updatable = false)
@JsonIgnore
private Member member;

我认为 @JsonIgnore 会忽略此属性,以免被检索到客户端。如果我需要这个属性及其子属性(如果它有子属性)怎么办?
是的,我有同样的问题。但是没有人回答我。
@KumaresanPerumal 试试这个stackoverflow.com/a/37394318/7042380
P
Prabu M

对我来说工作得很好Resolve Json Infinite Recursion problem when working with Jackson

这就是我在 oneToMany 和 ManyToOne 映射中所做的

@ManyToOne
@JoinColumn(name="Key")
@JsonBackReference
private LgcyIsp Key;


@OneToMany(mappedBy="LgcyIsp ")
@JsonManagedReference
private List<Safety> safety;

我在 Spring Boot 应用程序中使用了休眠映射
嗨作者,感谢您提供的精彩教程和精彩帖子。但是我发现 @JsonManagedReference@JsonBackReference 并没有为您提供与 @OneToMany@ManyToOne 场景相关的数据,在使用 @JsonIgnoreProperties 时也会跳过相关的实体数据。如何解决这个问题?
f
fabioresner

对我来说,最好的解决方案是使用 @JsonView 并为每个场景创建特定的过滤器。您也可以使用 @JsonManagedReference@JsonBackReference,但它是一种硬编码解决方案,仅适用于一种情况,即所有者始终引用拥有方,而不是相反。如果您有另一个序列化场景,您需要以不同的方式重新注释属性,您将无法这样做。

问题

让我们使用两个类,CompanyEmployee,它们之间存在循环依赖关系:

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` 的解决方案

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

并更改测试以使序列化程序使用视图:

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

这是一篇很好的文章,解释了解决递归的许多替代解决方案:baeldung.com/…
S
Shane

现在有一个 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


然后解决问题,因为它看起来很有趣。我和 OP 有同样的问题,包括上面的所有技巧都没有奏效。
i
ifelse.codes

@JsonIgnoreProperties 就是答案。

使用这样的东西::

@OneToMany(mappedBy = "course",fetch=FetchType.EAGER)
@JsonIgnoreProperties("course")
private Set<Student> students;

自信地使用它,因为我看到 Jhipster 在其生成的代码中使用它
感谢你的回答。但是我发现 @JsonManagedReference@JsonBackReference 并没有为您提供与 @OneToMany@ManyToOne 场景相关的数据,在使用 @JsonIgnoreProperties 时也会跳过相关的实体数据。如何解决这个问题?
S
Shubham

您应该将@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;

如果我把@jsonIgnore注解放在孩子里面。当我试图带孩子时,我无法从孩子那里得到父对象。为什么父对象不来,被@jsonignore忽略。告诉我从孩子到父母和父母到孩子的方式。
不需要使用@JsonIgnore,只需使用上面的注释和通过使用Getter 和setter 来获取父子对象。 Jsonignore 也在做同样的事情,但它会产生无限递归。如果您共享您的代码,那么我可以检查您为什么没有得到对象。因为对我来说,两者都来了。
我的意思是说。带父母的时候。父对象应该带有子对象。取子对象时。孩子应该和父母一起来。它在这种情况下不起作用。请你帮助我好吗?
K
Klapsa2503

在我的情况下,改变关系就足够了:

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

至:

@OneToMany
private List<Town> towns;

另一个关系保持原样:

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

我认为最好使用 Kurt 的解决方案。因为 JoinColumn 解决方案可能以未引用的数据死体结束。
这实际上是唯一帮助我的事情。高层没有其他解决方案有效。我仍然不确定为什么...
A
Arif Acar

我也遇到了同样的问题。我使用了 @JsonIdentityInfoObjectIdGenerators.PropertyGenerator.class 生成器类型。

这就是我的解决方案:

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

M
Mr. Polywhirl

确保在任何地方都使用 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);

R
RAJ KASHWAN

您可以使用@JsonIgnore,但这将忽略由于外键关系而可以访问的json数据。因此,如果您需要外键数据(大多数情况下我们需要),那么 @JsonIgnore 将无济于事。在这种情况下,请遵循以下解决方案。

您将获得无限递归,因为 BodyStat 类再次引用了 Trainee 对象

身体状态

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

因此,您必须在 Trainee 中注释/省略上述部分


就我而言,它不起作用。您能看一下:github.com/JavaHelper/issue-jackson-boot 吗?
L
Leandro Ferreira

非常重要:如果您使用的是 LOMBOK,请确保排除 Set、List 等集合的属性...

像这样:

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

k
kasee nadh reddy bojja

在做了更多分析后我也遇到了同样的问题,我知道,我们也可以通过在 OneToMany 注释中保留@JsonBackReference 来获取映射实体

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

D
Dahar Youssef

您可以使用 DTO 模式创建类 TraineeDTO 而无需任何注释休眠,您可以使用杰克逊映射器将 Trainee 转换为 TraineeDTO 并宾果游戏错误消息 disapeare :)


你能举个例子吗?
S
Scott Langeberg

如果您无法忽略该属性,请尝试修改该字段的可见性。在我们的例子中,我们有旧代码仍然提交具有关系的实体,所以在我的例子中,这是修复:

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private Trainee trainee;

如果我把@jsonIgnore注解放在孩子里面。当我试图带孩子时,我无法从孩子那里得到父对象。为什么父对象不来,被@jsonignore忽略。告诉我从孩子到父母和父母到孩子的方式。
M
Marco Aurélio Alves Puton

出于某种原因,就我而言,它不适用于 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;

@ToString 使用的依赖项是什么?
P
Paul Roub

如果您使用 @JsonManagedReference@JsonBackReference@JsonIgnore 注释,它会忽略某些字段并使用 Jackson JSON 解决无限递归。

但是如果你使用 @JsonIdentityInfo 也避免了无限递归并且你可以获得所有字段值,所以我建议你使用 @JsonIdentityInfo 注释。

@JsonIdentityInfo(generator= ObjectIdGenerators.UUIDGenerator.class, property="@id")

请参阅这篇文章 https://www.toptal.com/javascript/bidirectional-relationship-in-json 以更好地了解 @JsonIdentityInfo 注释。


e
emily

这篇文章:https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion 有完整的解释。

如果您在旧版本中使用 Jackson,可以尝试 @jsonmanagedreference + @jsonbackreference。如果您的 Jackson 高于 2(我知道 1.9 也不起作用),请尝试使用 @JsonIdentityInfo。


M
Mogsdad

我遇到了这个问题,但我不想在我的实体中使用注释,所以我通过为我的类创建一个构造函数来解决,这个构造函数不能引用回引用这个实体的实体。让我们说这个场景。

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

如果您尝试使用 @ResponseBody 将类 BA 发送到视图,则可能会导致无限循环。您可以在您的类中编写一个构造函数,并像这样使用您的 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 不允许它,至少在版本 3.6.10.Final 中,所以当我需要为了在视图中显示这两个实体,我执行以下操作。

public A getAById(int id); //THE A id

public List<B> getBsByAId(int idA); //the A id.

此解决方案的另一个问题是,如果添加或删除属性,则必须更新构造函数和所有查询。


M
Morik

如果您使用 Spring Data Rest,可以通过为循环引用中涉及的每个实体创建存储库来解决问题。


h
hello_earth

我来晚了,这已经是一个很长的话题了。但是我也花了几个小时试图弄清楚这一点,并想举我的例子作为另一个例子。

我尝试了 JsonIgnore、JsonIgnoreProperties 和 BackReference 解决方案,但奇怪的是,它们好像没有被选中。

我使用了 Lombok 并认为它可能会干扰,因为它创建了构造函数并覆盖了 toString(在 stackoverflowerror 堆栈中看到了 toString)。

最后,这不是 Lombok 的错——我使用 NetBeans 从数据库表中自动生成 JPA 实体,没有考虑太多——好吧,添加到生成的类中的注释之一是 @XmlRootElement。一旦我删除它,一切都开始工作了。那好吧。


z
zawhtut

关键是将@JsonIgnore 放在setter 方法中,如下所示。就我而言。

乡镇.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;
}

村庄.java

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "townshipId", insertable=false, updatable=false)
Township township;

@Column(name = "townshipId", nullable=false)
Long townshipId;

A
Anil Nivargi

我遇到了同样的问题,添加 jsonbackref 和 jsonmanagedref 并请确保 @override 等于和 hashCode 方法,这绝对可以解决这个问题。