Spring Boot JPA分级分类产品
Spring Boot JPA hierarchical category products
这是我的类别 table。
@Getter
@Setter
@NoArgsConstructor
@Entity
public class Category {
@Id
@GeneratedValue
private long id;
private String name;
private String imageFileName;
private int depth;
@ManyToOne
private Category parentCategory;
@OneToMany(mappedBy = "parentCategory")
private List<Category> childCategories;
@OneToMany(mappedBy = "category")
private List<Product> products;
private boolean deleted;
}
这里是仅适用于 0 和 1 深度类别的解决方案。
public List<Product> getByCategoryId(long categoryId) {
Category category = categoryService.getById(categoryId);
if (category.getDepth() == 0) {
return productRepository.findByCategoryParentCategoryAndDeletedFalse(category);
}
return productRepository.findByCategoryAndDeletedFalse(category);
}
我想按类别 ID 获取所有产品。例如,如果我想按深度为 10 的类别获取产品,我必须编写这么长的方法。我不想为每个深度都写方法。我该怎么做简化器?
可能有一种巧妙的方法可以通过查询来完成此操作,但这里有一些可行的方法,JPA 应该为您获取嵌入式实体,而您无需执行任何额外的查询。
基本思想是递归爬行直到达到一定深度。
public List<Product> getByCategoryId(long categoryId, int depth) {
Category category = categoryService.getById(categoryId);
Set<Product> result = new HashSet<>();
searchUntilDepth(result, category,depth);
return new ArrayList<>(result);
}
private void searchUntilDepth(Set<Product> foundProducts, Category cat, int depth) {
foundProducts.addAll(cat.getProducts());
if (cat.getDepth() >= depth) {
return;
}
for (Category child : cat.getChildCategories()) {
searchUntilDepth(foundProducts, child, depth);
}
}
我在我的代码中添加了最大深度检查。最大深度对我来说是 4。深度可以是 1 或 2 或 3 或 4。我的解决方案就是这样。目前看起来效果不错。
@Query("select p from Product p " +
"left join Category c1 on p.category.id = c1.id and c1.deleted = false " +
"left join Category c2 on c1.parentCategory.id = c2.id and c2.deleted = false " +
"left join Category c3 on c2.parentCategory.id = c3.id and c3.deleted = false " +
"left join Category c4 on c3.parentCategory.id = c4.id and c4.deleted = false " +
"where p.deleted = false and " +
"(c1.id = :#{#category.id} or c2.id = :#{#category.id} or c3.id = :#{#category.id} or c4.id = :#{#category.id})")
List<Product> getByCategoryAndDeletedFalse(@Param("category") Category category);
这是我的类别 table。
@Getter
@Setter
@NoArgsConstructor
@Entity
public class Category {
@Id
@GeneratedValue
private long id;
private String name;
private String imageFileName;
private int depth;
@ManyToOne
private Category parentCategory;
@OneToMany(mappedBy = "parentCategory")
private List<Category> childCategories;
@OneToMany(mappedBy = "category")
private List<Product> products;
private boolean deleted;
}
这里是仅适用于 0 和 1 深度类别的解决方案。
public List<Product> getByCategoryId(long categoryId) {
Category category = categoryService.getById(categoryId);
if (category.getDepth() == 0) {
return productRepository.findByCategoryParentCategoryAndDeletedFalse(category);
}
return productRepository.findByCategoryAndDeletedFalse(category);
}
我想按类别 ID 获取所有产品。例如,如果我想按深度为 10 的类别获取产品,我必须编写这么长的方法。我不想为每个深度都写方法。我该怎么做简化器?
可能有一种巧妙的方法可以通过查询来完成此操作,但这里有一些可行的方法,JPA 应该为您获取嵌入式实体,而您无需执行任何额外的查询。
基本思想是递归爬行直到达到一定深度。
public List<Product> getByCategoryId(long categoryId, int depth) {
Category category = categoryService.getById(categoryId);
Set<Product> result = new HashSet<>();
searchUntilDepth(result, category,depth);
return new ArrayList<>(result);
}
private void searchUntilDepth(Set<Product> foundProducts, Category cat, int depth) {
foundProducts.addAll(cat.getProducts());
if (cat.getDepth() >= depth) {
return;
}
for (Category child : cat.getChildCategories()) {
searchUntilDepth(foundProducts, child, depth);
}
}
我在我的代码中添加了最大深度检查。最大深度对我来说是 4。深度可以是 1 或 2 或 3 或 4。我的解决方案就是这样。目前看起来效果不错。
@Query("select p from Product p " +
"left join Category c1 on p.category.id = c1.id and c1.deleted = false " +
"left join Category c2 on c1.parentCategory.id = c2.id and c2.deleted = false " +
"left join Category c3 on c2.parentCategory.id = c3.id and c3.deleted = false " +
"left join Category c4 on c3.parentCategory.id = c4.id and c4.deleted = false " +
"where p.deleted = false and " +
"(c1.id = :#{#category.id} or c2.id = :#{#category.id} or c3.id = :#{#category.id} or c4.id = :#{#category.id})")
List<Product> getByCategoryAndDeletedFalse(@Param("category") Category category);