如何查询具有未定义深度的层次结构类别树实体

Ere*_*ent 9 java tree spring-boot

我使用springboot为购物网站创建了一个层次结构或树形结构.我的问题是在寻找特定产品及其父母时如何查询这样的结构:

id,  category_name,   parent_id
'1', 'Electronics',      NULL
'2', 'Gaming',           NULL
'3', 'Home Audio',       '1'
'4', 'Console',          '2'
'5', 'Sony',             '4'
'6', 'Karaoke',          '3'
Run Code Online (Sandbox Code Playgroud)

这就是我所做的,关于我需要在实体上实现这个结构的任何指针,以及我如何查询它,即

同样重要的是要注意我正在使用postgres数据库

  1. findAllProducts在一个类别和
  2. 查找链接到产品的所有类别.

类别实体

@Entity
@Table(name = "categories")
public class Category {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private String categoryName;

        @ManyToOne
        @JoinColumn(name = "parent_id")
        private Category parent;

        @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE, orphanRemoval = true)
        private List<Category> children = new ArrayList<Category>();

    // Getter and setter removed for readability    
}
Run Code Online (Sandbox Code Playgroud)

产品实体

@Entity
@Table(name = "products")
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    @Version
    @Column(name = "version")
    private Integer version;

    private String name;

    private int quantity;

    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "products_categories", joinColumns = {
            @JoinColumn(name = "product_id", referencedColumnName = "id") }, inverseJoinColumns = {
                    @JoinColumn(name = "category_id", referencedColumnName = "id") })
    private List<Category> categories;

  // getters and setter omitted for readability

}
Run Code Online (Sandbox Code Playgroud)

分类服务

public class CategoryService {

@Autowired
private CategoryRepository categoryRepository;

public void addCategory(Category category) {
    categoryRepository.save(category);
}
}
Run Code Online (Sandbox Code Playgroud)

产品服务

public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    public void addProduct(Product product) {
        productRepository.save(product);
    }
   }
Run Code Online (Sandbox Code Playgroud)

Pie*_*ido 4

您可以使用递归查询来选择给定类别的父级。

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface CategoryRepository extends JpaRepository<Category, Long> {

  @Query(
      value = "WITH RECURSIVE ancestors(id, parent_id, category_name, lvl) AS ("
          + "   SELECT cat.id, cat.parent_id, cat.category_name, 1 AS lvl "
          + "   FROM categories cat "
          + "   WHERE cat.id = :categoryId "
          + "   UNION ALL "
          + "   SELECT parent.id, parent.parent_id, parent.category_name, child.lvl + 1 AS lvl "
          + "   FROM categories parent "
          + "   JOIN ancestors child "
          + "   ON parent.id = child.parent_id "
          + " )"
          + "SELECT category_name from ancestors ORDER BY lvl DESC"
      , nativeQuery = true)
  List<String> findAncestry(@Param("categoryId") Long categoryId);
}
Run Code Online (Sandbox Code Playgroud)

以下是创建类别层次结构并对其进行查询的测试:

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class HierarchyTest {

  @Autowired
  CategoryRepository categoryRepository;

  @Test
  public void testCategoryRepository() {
    String[] electronicsCategoryNames = {"Electronics", "Home Audio", "Karaoke"};
    String[] gamingCategoryNames = {"Gaming", "Console", "Sony"};

    Category karaoke = createCategories(electronicsCategoryNames);
    Category sony = createCategories(gamingCategoryNames);

    findAncestry(karaoke, electronicsCategoryNames);
    findAncestry(sony, gamingCategoryNames);
  }

  Category createCategories(String[] categoryNames) {
    Category parent = null;

    for (String categoryName : categoryNames) {
      Category category = new Category();
      category.setCategoryName(categoryName);
      category.setParent(parent);
      parent = categoryRepository.save(category);
    }

    Assert.assertNotNull("category.id", parent.getId());
    return parent;
  }


  void findAncestry(Category category, String[] categoryNames) {
    List<String> ancestry = categoryRepository.findAncestry(category.getId());
    Assert.assertEquals("ancestry.size", categoryNames.length, ancestry.size());

    for (int i = 0; i < categoryNames.length; i++) {
      Assert.assertEquals("ancestor " + i, categoryNames[i], ancestry.get(i));
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这是在 H2 和 PostgreSQL 数据库上进行测试的。您可能需要根据您使用的实际数据库修改本机查询。

PostgreSQL 有关于其工作原理的优秀文档,请参阅:

https://www.postgresql.org/docs/11/queries-with.html