我有两个实体,一个 Store 和一个 ProductCategory ,我坚持并希望从 JpaRepository 中使用 @RepositoryRestResource 。商店有一系列产品类别,产品类别与自己有父/子关系。此外,我还希望在向Store的REST存储库发出GET请求时嵌入产品类别层次结构。
实体看起来如下:
商店:
@Entity
@Table(name = "store")
@EqualsAndHashCode(exclude="categories", callSuper = true)
@ToString(exclude="categories", callSuper = true)
public class Store extends BaseEntity {
@Getter
@Setter
@Column(name = "name")
private String name;
@OneToMany(targetEntity = ProductCategory.class, cascade = CascadeType.ALL, mappedBy = "store")
@Getter
private List<ProductCategory> categories = new ArrayList<>();
public void setCategories(List<ProductCategory> categories) {
for (ProductCategory category : categories) {
if (category.getStore() == null || !category.getStore().equals(this)) {
category.setStore(this);
}
}
this.categories = categories;
}
}
ProductCategory.java:
@Entity
@Table(name = "product_category")
@EqualsAndHashCode(exclude={"store", "children", "parent"}, callSuper = true)
@ToString(exclude={"store", "children", "parent"}, callSuper = true)
public class ProductCategory extends BaseEntity {
@Getter @Setter
@Column(name="name")
private String name;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="store_id", nullable = true)
@Getter
@JsonIgnore
@RestResource(exported = false)
private Store store;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="parent", nullable = true)
@Getter
@JsonIgnore
@RestResource(exported = false, path = "parent")
private ProductCategory parent;
@OneToMany(targetEntity = ProductCategory.class, cascade=CascadeType.ALL, mappedBy="parent")
@Getter
@RestResource(rel = "children", path = "children")
private List<ProductCategory> children = new ArrayList<>();
public void setStore(Store store) {
this.store = store;
if(!store.getCategories().contains(this)) {
store.getCategories().add(this);
}
}
public void setParent(ProductCategory parent) {
this.parent = parent;
if(!parent.getChildren().contains(this)) {
parent.getChildren().add(this);
}
}
public void setChildren(List<ProductCategory> children) {
for(ProductCategory child : children) {
if(child.getParent() == null || !child.getParent().equals(this)) {
child.setParent(this);
}
}
this.children = children;
}
}
BaseEntity 为其他实体提供唯一标识符。每个实体都有自己的存储库:
StoreRepository:
@RepositoryRestResource(collectionResourceRel = "store", path = "store")
public interface StoreRepository extends JpaRepository<Store, Long>, JpaSpecificationExecutor<Store> {
Store findById(@Param("id") Long id);
Optional<Store> findFirstByName(@Param("name") String name);
}
ProductCategoryRepository:
@RepositoryRestResource(collectionResourceRel = "productCategory", path = "productCategory", exported = false)
public interface ProductCategoryRepository extends JpaRepository<ProductCategory, Long>, JpaSpecificationExecutor<ProductCategory> {
ProductCategory findById(@Param("id") Long id);
List<ProductCategory> findByName(@Param("name") String name);
}
在查询特定商店后,我希望以JSON格式收到的结果如下所示:
{
id: 0,
name: 'Pet Store',
categories: [
{
id: 0,
name: 'Mammal',
children: [
{
id: 2,
name: 'Dog',
children: [
{
id: 4,
name: 'Shiba',
children: []
},
{
id: 5,
name: 'Husky',
children: []
}
]
},
{
id: 3,
name: 'Cat',
children: []
}
]
},
{
id: 1,
name: 'Bird',
children: []
}
]
}
为简洁起见,删除了HATEOS关系。虽然可以很好地创建和从数据库中读取对象,但是当尝试通过针对REST资源的GET请求检索存储时,会产生以下错误:
restGetStoreProductsEmbedded(com.synthapp.embeddedproject.com.repo.StoreRepositoryTest):请求处理失败;嵌套异常是org.springframework.http.converter.HttpMessageNotWritableException:无法写入内容:无限递归(StackOverflowError)(通过引用链:org.springframework.hateoas.PagedResources [&#34; _embedded&#34;]);嵌套异常是com.fasterxml.jackson.databind.JsonMappingException:无限递归(StackOverflowError)(通过引用链:org.springframework.hateoas.PagedResources [&#34; _embedded&#34;])
我试图通过以下方法解决这个问题:
在我的GitHub
中可以找到使用内存数据库实现此示例的项目答案 0 :(得分:0)
您可以为ProductCategory
创建一个投影,并将Store
退出。
http://docs.spring.io/spring-data/rest/docs/current/reference/html/#projections-excerpts
请务必阅读有关摘录的部分,这可能非常有用。
我创建了使用PersistenEntityResourceAssembler
的控制器(因为你不希望暴露实体但你想要返回资源),这将自动将投影应用于资源而不需要在URL中传递它 - 但只有在存储库中通过摘录定义它。