当前位置: 首页 > 知识库问答 >
问题:

双向关系产生JSON无限递归的Spring数据REST投影

夏侯弘量
2023-03-14

我在一个使用SpringBoot2.0、Hibernate和SpringDataREST的项目中工作。前端带有React
我遇到过这样的情况:一个用户可以与多家公司(他拥有多家公司)链接
当我尝试使用UserRepository或CompanyRepository获取一些实体时,我得到了错误:无法写入JSON:Infinite recursion(StackOverflowerError);嵌套的异常是com。fasterxml。杰克逊。数据绑定。JsonMappingException:无限递归(StackOverflowerr)

我必须使用投影来限制数据进入前端,因为我需要由投影自动生成的实体链接。

遵循实体:

@Entity
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_user")
    protected Long id;

    @OneToMany(cascade= { CascadeType.MERGE }, fetch = FetchType.EAGER, mappedBy="user")
    private List<Company> companyList;

    // Other data
    // Getters and Setters
}

@Entity
public class Company extends CadastroEmpresaUnica {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_company")
    protected Long id;

    @ManyToOne(cascade= { CascadeType.MERGE })
    @JoinColumn(name="id_user", nullable = false)
    private User user;

    // Other data
    // Getters and Setters
}

预测:

@Projection(name = "userProjection", types = { User.class })
public interface UserProjection {

    List<CompanyProjection> getCompanyList();

    // Other Getters
}

@Projection(name = "companyProjection", types = { Company.class }) 
public interface CompanyProjection {
    UserProjection getUser();

    // Other Getters
}

我们正在使用的存储库之一:

@RepositoryRestResource(collectionResourceRel = "company", path = "companies", excerptProjection = CompanyProjection.class)
public interface CompanyRepository extends PagingAndSortingRepository<Company, Long>, CompanyRepositoryCustom, JpaSpecificationExecutor<Company> {}

在搜索双向无限递归时,我发现了关于@JsonManaged参考和@JsonBack参考的内容,这些内容总是直接在实体中使用。所以我试着在我的投影中使用,它起作用了。所以它解决了我的无限递归问题,但是它产生了另一个问题,我不能从我的公司访问我的用户(因为显然“@JsonBack参考”不能让它停止递归)。
这是我的投影与这个解决方案:

@Projection(name = "userProjection", types = { User.class })
public interface UserProjection {
    @JsonManagedReference
    List<CompanyProjection> getCompanyList();

    // Other Getters
}

@Projection(name = "companyProjection", types = { Company.class }) 
public interface CompanyProjection {
    @JsonBackReference
    UserProjection getUser();

    // Other Getters
}

搜索多一点,我读到'@JsonIdtyInfo',再次,在实体中使用。所以我试图删除其他Json注释,并在我的投影中使用@JsonIdtyInfo。如下例所示:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
@Projection(name = "userProjection", types = { User.class })
public interface UserProjection {
    Long getId();
    List<CompanyProjection> getCompanyList();

    // Other Getters
}

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
@Projection(name = "companyProjection", types = { Company.class }) 
public interface CompanyProjection {
    Long getId();
    UserProjection getUser();

    // Other Getters
}

它不起作用。现在Json无限递归再次发生
我是SpringDataREST新手,我正在阅读SpringDocumentation和Stackoverflow主题,试图更好地理解SpringDataREST的投影。我想知道我做错了什么,当然,如果我以错误的方式使用投影,但是我需要继续这个项目。

共有3个答案

何兴学
2023-03-14

我们找到的最好的方法是使用Jackson注释@JsonIgnoreProperties,它应该在父列表中使用,以便在子列表中显示自己。但是,经过几次尝试之后,这个注释似乎在投影中不起作用,特别是在Spring数据REST中
按照正确方法的示例进行操作:

@Projection(name = "userProjection", types = { User.class })
public interface UserProjection {
    @JsonIgnoreProperties({"user"})
    List<CompanyProjection> getCompanyList();

    // Other Getters
}

@Projection(name = "companyProjection", types = { Company.class }) 
public interface CompanyProjection {
    UserProjection getUser();

    // Other Getters
}

我们发送了一张关于这个Spring数据REST问题的票证,它已经被接受。我们相信在不久的将来,它将得到纠正,我们可以使用它
现在,我们调整投影,以便列表对象可以使用原始投影的“派生”,忽略导致无限递归的属性
遵循以下示例:

@Projection(name = "userProjection", types = { User.class })
public interface UserProjection {
    List<CompanyProjectionWithoutUser> getCompanyList();

    // Other Getters

    // Projection without the User, that couses infinite recursion
    public interface CompanyProjectionWithoutUser extends CompanyProjection {
        @Override
        @JsonIgnore
        UserProjection getUser();
    }
}

@Projection(name = "companyProjection", types = { Company.class }) 
public interface CompanyProjection {
    UserProjection getUser();

    // Other Getters
}
田彬郁
2023-03-14

有点难看,但简单的解决方案可能是再进行一次投影(例如,CompanyWithoutUserProjection),这可以停止递归

CompanyProjection {
  UserProjection getUser();
  //other getters
}


UserProjection {
  List<CompanyWithoutUserProjection> getCompanyList();
  //other getters
}


CompanyWithoutUserProjection {
  //other getters
}
严正初
2023-03-14

首先,SDR不喜欢双向关系

要解决这种情况,请尝试在公司实体中的User getter中添加以下内容:

@RestResource(exported = false)
@JsonIgnore
public User getUser() {...}

您也可以尝试使用注释@JsonIgnoreProperties。

此外,我认为如果你只需要得到用户的公司(和公司的用户),你不需要使用预测。SDR中的关联资源按如下方式导出(在您的示例中):

/users/{id}/companies
/companies/{id}/users
 类似资料:
  • 首先,这是我的实体。 玩家: 团队: 正如已经说明的许多主题一样,您可以通过Jackson以多种方式避免WebService中的StackOverflow Exeption。 这很酷,除了JPA之外,在序列化之前,所有的实体都可以无限递归地构造另一个实体。这只是丑陋的ans请求需要更长的时间。检查这个截图:IntelliJ调试器 有办法解决吗?知道我希望根据endpoint获得不同的结果。示例:

  • 我用的是Spring Roo 1.2.1和Jackson 1.9.7。在使用json序列化我的类时,我得到了一个JsonMappingException。 我读了以下帖子,但没有找到适合我的工作解决方案: Jackson的无限递归 Jackson-具有双方向关系的实体序列化(避免循环) 我不知道为什么JsonIgnore在属性QueueOuts的类Queue中不起作用。我也尝试了JsonManag

  • 问题内容: 我使用Gson 库将Java对象转换为Json响应…问题是,在JPA请求之后,由于与其他实体的递归关系,无法转换从DB检索到的对象: 我的源代码: 如您在这里看到的,我做了: 只是通过为coordonneesList中的每个GPS对象设置null来消除递归关系。 您认为这是一个很好的解决方案,或者还有其他更实用的方法吗?谢谢 问题答案: 有一个名为GraphAdapterBuilder

  • 我在和之间具有双向多对多关系。在的帮助下创建两个实体,甚至创建它们的子实体都是简单明了的。 和都可以在的帮助下轻松地自动持久化,如下所示: 然而...更新会导致异常: 我尝试修改的以包括、和/或,但未成功。我们怎么绕过这件事?

  • 在尝试执行GET到发布者存储库时,我正在执行GET和无限循环。 出版商: 书: 完整代码可在此处获得: https://github.com/vanyasav/library

  • 我在谷歌上搜索过,找不到任何可以在O(1)时间内存储和读取双向数据的DS。例如书籍和作家。有了书的名字,就必须找到作者。有了作者的名字,就必须找到书。 在数据库中,这些关系(如联接表)是如何存储的? 提前谢谢。