【问题标题】:Infinite loop with spring-boot in a one to many relation具有一对多关系的弹簧引导的无限循环
【发布时间】:2015-09-02 17:32:08
【问题描述】:

在一个 rest 应用程序中,我使用带有 jpa 的 spring boot。

我有一个班级寄宿家庭

谁有

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "lodger")
private List<Reference> referenceList;

在我的课堂参考中,我有

@ManyToOne
@JoinColumn(name = "lodgerId")
private Lodger lodger;

当我调用这个方法时

@RequestMapping(value = "/lodgers/{lodgerId}", method = RequestMethod.GET)
public Lodger getLogderById(@PathVariable("lodgerId") long lodgerId) {
    return lodgerService.getLodger(lodgerId);
}

我收到此错误

org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Infinite recursion (StackOverflowError) (through reference chain: server.bean.Lodger["referenceList"]->org.hibernate.collection.internal.PersistentBag[0]->server.bean.Reference["lodger"]->server.bean.Lodger["referenceList"]->org.hibernate.collection.internal.PersistentBag[0]->server.bean.Reference["lodger"]->server.bean.Lodger["referenceList"]...

【问题讨论】:

标签: spring spring-boot spring-restcontroller


【解决方案1】:

当您在返回对象中有一个循环并且 spring 尝试将其序列化为其他类型时,就会发生这种情况。

尝试从返回的模型中创建没有循环的 DTO 或值对象(简单 POJO),然后返回它。

【讨论】:

  • 您能否在示例中详细解释一下!
【解决方案2】:

不要通过 REST web 服务返回具有循环依赖关系的实体 - 创建新的 DTO 类,映射从数据库中获取的实体并在 web 服务中返回它。

更多信息在这里:http://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application

当然,如果你愿意,你可以使用另一个映射库,我个人最喜欢的是 Orika (http://orika-mapper.github.io/orika-docs/intro.html)

【讨论】:

    【解决方案3】:

    假设您的代码如下所示:-

    Lodger.class
    
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "lodger")
    private List<Reference> referenceList;
    
    public List<Reference> getReferenceList() {
        return referenceList;
    }
    
    public void setReferenceList(List<Reference> referenceList) {
        this.referenceList = referenceList;
    }
    
    @Override
    public String toString() {
        return "Lodger[referenceList=" + referenceList + "]";
    }
    

    Reference.class

    @ManyToOne
    @JoinColumn(name = "lodgerId")
    private Lodger lodger;
    
    public Lodger getLodger() {
        return lodger;
    }
    
    public void setLodger(Lodger lodger) {
        this.lodger = lodger;
    }
    
    @Override
    public String toString() {
        return "Reference[lodger=" + lodger + "]";
    }
    

    当您注意到在两个 POJO 中编写的 toString() 方法时,您会看到我们正在从任一侧调用两个类的 toString(),这会导致无穷大。从永远不会终止的双方对 toString() 方法的调用。为了避免这种情况,请从 Reference.class 的 toString() 中删除任何引用[您也可以从 Lodger 类中删除。] 所以 Reference 类的 toString() 中不会有 lodger 属性。

    所以最后你的 Reference 类看起来像:-

    Reference.class

    @ManyToOne
    @JoinColumn(name = "lodgerId")
    private Lodger lodger;
    
    public Lodger getLodger() {
        return lodger;
    }
    
    public void setLodger(Lodger lodger) {
        this.lodger = lodger;
    }
    
    @Override
    public String toString() {
        return "Reference[Properties other than lodger=" + properties other than lodger + "]";
    }
    

    【讨论】:

    • 如果您还没有得到解决方案,请发表评论。
    【解决方案4】:

    解决方案:

    使用

    @JsonManagedReference 为第一个实例化的对象注解

    @JsonBackReference 为实例化的第二个对象注解

    第一:

    @JsonManagedReference
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "lodger")
        private List<Reference> referenceList;
    

    第二:

    @JsonBackReference
    @ManyToOne
        @JoinColumn(name = "lodgerId")
        private Lodger lodger;
    

    【讨论】:

    • 但这不会给我第二个对象的结果中的 lodgerId
    • 这个解决方案解决了主要问题,但产生了另一个问题,即现在我们根本没有得到第二个引用的对象。
    【解决方案5】:

    如果两个表中的主键名称相同,例如:id。

    添加这个

    @Entity
    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
    public class User {
        ...
    }
    

    并引用类。

    @Entity
    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
    public class Reference {
        ...
    }
    

    【讨论】:

      【解决方案6】:
      @JsonIgnoreProperties({"hibernateLazyInitializer","referenceList"}) at class Level
      

      参考见this article on medium.com

      【讨论】:

        【解决方案7】:

        您唯一需要的是,在您有注释 @ManyToOne 的类中,使用要在值部分中跳过的属性实现下一个注释 @JsonIgnoreProperties(value = {"yourAttribute", "handler", "hibernateLazyInitializer"}, allowSetters = true)

        我为你的代码举了一个例子->

        @ManyToOne(fetch = FetchType.LAZY)
        @JsonIgnoreProperties(value = {"referenceList", "handler","hibernateLazyInitializer"}, allowSetters = true)
        @JoinColumn(name = "lodgerId")
        private Lodger lodger;
        

        您在@JsonIgnoreProperties 的值部分中放置的所有属性都将被忽略,这样您就可以解决无限循环并将​​其用于将来具有相同格式的其他开发。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-07-22
          • 2018-12-23
          • 1970-01-01
          • 2018-10-02
          • 2019-09-12
          • 1970-01-01
          相关资源
          最近更新 更多