具有双向关系的Spring Data REST Projection导致JSON无限递归

时间:2018-04-12 18:41:06

标签: json spring hibernate recursion projection

我在使用Spring Boot 2.0,Hibernate和Spring Data REST的项目中工作。 FrontEnd with React。
我认为用户可以与多家公司联系(他拥有多家公司) 当我尝试使用UserRepository或CompanyRepository获取某些实体时,我收到错误:无法写入JSON:无限递归(StackOverflowError);嵌套异常是com.fasterxml.jackson.databind.JsonMappingException:无限递归(StackOverflowError)。

我必须使用Projections限制前往FrontEnd的数据,因为我需要指向实体的链接,由Projections自动生成。

关注实体:

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

搜索双向无限递归我发现了关于&#39; @JsonManagedReference&#39;的内容。和&#39; @JsonBackReference&#39;,始终直接在实体中使用。所以我试着在我的预测中使用它并且它有效。所以它解决了我的无限递归问题,但它产生了另一个问题,我无法从我的公司访问我的用户(因为显然&#39; @JsonBackReference&#39;不能让它停止递归)。 /> 以下是此解决方案的预测:

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

再多搜索一下我在实体中使用过的@ JsonIdentityInfo&#39;所以我试图删除其他Json注释并使用&#39; @JsonIdentityInfo&#39;在我的投影中。如下例所示:

@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无限递归再次发生 我是Spring Data REST的新手,我真的想通过Spring Data Rest了解更好的Projections,阅读Spring文档和Stackoverflow主题。我想知道我做错了什么,当然,如果我以错误的方式使用预测,但我需要继续这个项目。

3 个答案:

答案 0 :(得分:1)

首先是SDR docs双向关系))

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

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

您也可以尝试使用注释does not like

此外,我认为如果您只需要获得用户的公司(以及公司的用户),您就不需要使用预测。 SDR中的相关资源就像这样导出(在您的情况下):

/users/{id}/companies
/companies/{id}/users

答案 1 :(得分:1)

有点难看,但简单的解决方案可能是另一个投影(例如CompanyWithoutUserProjection),它可以阻止你的递归。

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


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


CompanyWithoutUserProjection {
  //other getters
}

答案 2 :(得分:1)

我们找到的最好方法是使用Jackson注释@JsonIgnoreProperties,这应该在父列表中使用,以便在孩子身上使用自己。但是,经过几次尝试后,似乎这个注释不适用于预测,特别是对于Spring Data 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 Data REST问题发送ticket并已被接受。我们相信在不久的将来它会被纠正,我们可以使用它 现在,我们调整我们的投影,以便列表对象可以使用原始投影的“推导”,忽略导致无限递归的属性。
按照示例:

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