【问题标题】:Infinite Recursion with Jackson JSON and Hibernate JPA issue杰克逊 JSON 和 Hibernate JPA 问题的无限递归
【发布时间】:2011-03-20 12:24:42
【问题描述】:

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

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

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

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

代码sn-ps:

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

persistence.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>

【问题讨论】:

标签: java json orm spring-mvc jackson


【解决方案1】:

您可以使用@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,当您更新实体时,您将在“外键”中为空...
【解决方案2】:

此外,Jackson 1.6 支持 handling bi-directional references... 看起来像 你在找什么(this blog entry 也提到了这个功能)

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

【讨论】:

  • 链接已失效,您介意更新它们还是编辑您的答案。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    JsonIgnoreProperties [2017 更新]:

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

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


    JsonManagedReference 和 JsonBackReference

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

    说明

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

    所以,Jackson 将引用的前半部分(您在 Trainee 类中的 Set&lt;BodyStat&gt; 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 响应并查看代码中发生了什么。

    来源:

    【讨论】:

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

    此外,使用 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 中删除映射属性。
    【解决方案6】:

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

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

    到:

    @OneToMany
    private List<Town> towns;
    

    另一个关系保持原样:

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

    【讨论】:

    • 我认为最好使用 Kurt 的解决方案。因为 JoinColumn 解决方案可能以未引用的数据死体结束。
    • 这实际上是唯一对我有帮助的东西。高层没有其他解决方案有效。我仍然不确定为什么......
    【解决方案7】:

    现在有一个 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 有同样的问题,包括上面的所有技巧都没有奏效。
    【解决方案8】:

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

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

    【讨论】:

    • 我认为@JsonIgnore 忽略了这个属性,不会被检索到客户端。如果我需要这个属性及其子属性(如果它有子属性)怎么办?
    • 是的,我也有同样的问题。但是没有人回答我。
    【解决方案9】:

    你可以使用 DTO 模式 创建没有任何注释 hiberbnate 的 TraineeDTO 类,您可以使用 jackson mapper 将 Trainee 转换为 TraineeDTO 并显示错误消息:)

    【讨论】:

      【解决方案10】:

      我遇到了这个问题,但是我不想在我的实体中使用注释,所以我通过为我的类创建一个构造函数来解决,这个构造函数不能引用回引用这个实体的实体。假设是这种情况。

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

      如果您尝试将BA 类与@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 不允许这样做,至少在 版本 3.6.10.Final,所以当我需要在视图中显示两个实体时,我会执行以下操作。

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

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

      【讨论】:

        【解决方案11】:

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

        【讨论】:

          【解决方案12】:

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

            新注释@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 用于代码示例,作为 Jackson 新手,阅读 JavaDoc 并不清楚 @JsonIgnoreProperties 的使用
            【解决方案14】:

            对我来说,最好的解决方案是使用@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;
                }
            }
            

            以及尝试使用ObjectMapperSpring 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/…
            【解决方案15】:

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

            这是我的解决方案:

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

            【讨论】:

              【解决方案16】:

              你可以使用@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

              中注释/省略上述部分

              【讨论】:

              【解决方案17】:

              @JsonIgnoreProperties 就是答案。

              使用类似这样的东西 ::

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

              【讨论】:

              • 自信地使用它,因为我看到 Jhipster 在其生成的代码中使用它
              • 感谢您的回答。但是我发现@JsonManagedReference@JsonBackReference 不会为您提供与@OneToMany@ManyToOne 场景相关的数据,在使用@JsonIgnoreProperties 时也会跳过相关的实体数据。如何解决?
              【解决方案18】:

              对我来说工作得很好 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;
              

              【讨论】:

              • 我在spring boot应用中使用过hibernate映射
              • 您好作者,感谢您提供的精彩教程和精彩帖子。但是我发现@JsonManagedReference@JsonBackReference 不会为您提供与@OneToMany@ManyToOne 场景关联的数据,在使用@JsonIgnoreProperties 时也会跳过关联的实体数据。如何解决?
              【解决方案19】:

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

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

              【讨论】:

              • 如果我把@jsonIgnore注解放在子里。当我试图带孩子时,我无法从孩子那里得到父对象。为什么父对象不来,被@jsonignore忽略。告诉我从孩子到父母和父母到孩子的方式。
              【解决方案20】:

              您应该将@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 也在做同样的事情,但它会产生无限递归。如果您共享您的代码,那么我可以检查您为什么没有得到对象。因为对我来说两者都来了。
              • 我的意思是说。带父母的时候。父对象应该带有子对象。取子对象时。孩子应该和父母一起来。它在这种情况下不起作用。你能帮帮我吗?
              【解决方案21】:

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

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

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

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

              【讨论】:

                【解决方案22】:

                重点是将 @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】:

                  出于某种原因,就我而言,它不适用于 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 使用的依赖是什么?
                  【解决方案24】:

                  在做了更多分析后我也遇到了同样的问题,我知道,我们也可以通过在 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;
                  

                  【讨论】:

                    【解决方案25】:

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

                    像这样:

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

                    【讨论】:

                      【解决方案26】:

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

                      【讨论】:

                        【解决方案27】:

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

                        但是如果你使用@JsonIdentityInfo,它也避免了无限递归,你可以得到所有的字段值,所以我建议你使用@JsonIdentityInfo注解。

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

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

                        【讨论】:

                          【解决方案28】:

                          此帖:https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion 有完整的解释。

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

                          【讨论】:

                            猜你喜欢
                            • 2015-11-28
                            • 2016-05-22
                            • 1970-01-01
                            • 2018-10-02
                            相关资源
                            最近更新 更多