Spring 具有双向关系的数据 REST 投影导致 JSON 无限递归
Spring Data REST Projection with bidirectional relationship resulting JSON infinite recursion
我在一个使用 Spring Boot 2.0、Hibernate 和 Spring Data REST 的项目中工作。带有 React 的前端。
我的情况是用户可以与多家公司相关联(他拥有多家公司)。
当我尝试使用 UserRepository 或 CompanyRepository 获取某些实体时,出现错误:无法写入 JSON:无限递归 (WhosebugError);嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException: 无限递归 (WhosebugError)。
我必须使用投影来限制进入前端的数据,因为我需要实体的链接,由投影自动生成。
关注实体:
@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> {}
搜索双向无限递归我发现了关于“@JsonManagedReference”和“@JsonBackReference”的内容,它们总是直接在实体中使用。所以我尝试在我的投影中使用它并且它起作用了。所以它解决了我的无限递归问题,但它产生了另一个问题,我无法从我的公司访问我的用户(因为显然 '@JsonBackReference' 没有让它停止递归)。
这是我对此解决方案的预测:
@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”的信息,它再次被用在实体中。所以我试图删除其他 Json 注释并在我的投影中使用 '@JsonIdentityInfo'。如以下例子:
@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 文档和 Whosebug 主题来更好地理解 Spring Data Rest 的投影。我想知道我做错了什么,当然,如果我以错误的方式使用投影,但我需要继续这个项目。
首先是 SDR 双向关系 ))
要解决这种情况,请尝试向公司实体中的用户 getter 添加以下内容:
@RestResource(exported = false)
@JsonIgnore
public User getUser() {...}
你也可以尝试使用注解。
此外,我认为如果您只需要获取用户的公司(以及公司的用户),则不需要使用投影。 SDR 中的关联资源是这样导出的(在您的情况下):
/users/{id}/companies
/companies/{id}/users
有点难看,但简单的解决方案可能是多一个投影(例如 CompanyWithoutUserProjection),它可以停止您的递归。
CompanyProjection {
UserProjection getUser();
//other getters
}
UserProjection {
List<CompanyWithoutUserProjection> getCompanyList();
//other getters
}
CompanyWithoutUserProjection {
//other getters
}
我们找到的最好方法是使用 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 数据 REST 问题发送了一个 ticket,它已被接受。我们相信在不久的将来它会得到纠正,我们可以使用它。
现在,我们调整投影,使列表对象可以使用原始投影的 "derivation",忽略导致无限递归的 属性。
跟例:
@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
}
我在一个使用 Spring Boot 2.0、Hibernate 和 Spring Data REST 的项目中工作。带有 React 的前端。
我的情况是用户可以与多家公司相关联(他拥有多家公司)。
当我尝试使用 UserRepository 或 CompanyRepository 获取某些实体时,出现错误:无法写入 JSON:无限递归 (WhosebugError);嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException: 无限递归 (WhosebugError)。
我必须使用投影来限制进入前端的数据,因为我需要实体的链接,由投影自动生成。
关注实体:
@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> {}
搜索双向无限递归我发现了关于“@JsonManagedReference”和“@JsonBackReference”的内容,它们总是直接在实体中使用。所以我尝试在我的投影中使用它并且它起作用了。所以它解决了我的无限递归问题,但它产生了另一个问题,我无法从我的公司访问我的用户(因为显然 '@JsonBackReference' 没有让它停止递归)。
这是我对此解决方案的预测:
@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”的信息,它再次被用在实体中。所以我试图删除其他 Json 注释并在我的投影中使用 '@JsonIdentityInfo'。如以下例子:
@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 文档和 Whosebug 主题来更好地理解 Spring Data Rest 的投影。我想知道我做错了什么,当然,如果我以错误的方式使用投影,但我需要继续这个项目。
首先是 SDR
要解决这种情况,请尝试向公司实体中的用户 getter 添加以下内容:
@RestResource(exported = false)
@JsonIgnore
public User getUser() {...}
你也可以尝试使用注解
此外,我认为如果您只需要获取用户的公司(以及公司的用户),则不需要使用投影。 SDR 中的关联资源是这样导出的(在您的情况下):
/users/{id}/companies
/companies/{id}/users
有点难看,但简单的解决方案可能是多一个投影(例如 CompanyWithoutUserProjection),它可以停止您的递归。
CompanyProjection {
UserProjection getUser();
//other getters
}
UserProjection {
List<CompanyWithoutUserProjection> getCompanyList();
//other getters
}
CompanyWithoutUserProjection {
//other getters
}
我们找到的最好方法是使用 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 数据 REST 问题发送了一个 ticket,它已被接受。我们相信在不久的将来它会得到纠正,我们可以使用它。
现在,我们调整投影,使列表对象可以使用原始投影的 "derivation",忽略导致无限递归的 属性。
跟例:
@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
}