java - 如何查询具有未定义深度的分层类别树实体

标签 java spring-boot tree

我使用 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'

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

It is also important to note that i am using postgres database

  1. 找到一个类别中的所有产品
  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    
}

产品实体

@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

}

分类服务

public class CategoryService {

@Autowired
private CategoryRepository categoryRepository;

public void addCategory(Category category) {
    categoryRepository.save(category);
}
}

产品服务

public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    public void addProduct(Product product) {
        productRepository.save(product);
    }
   }

最佳答案

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

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);
}

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

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));
    }
  }
}

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

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

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

关于java - 如何查询具有未定义深度的分层类别树实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53128781/

相关文章:

javascript - XHR 在 Java 中加载 GET 失败?

java - 引用的 Javadoc 未显示在 Eclipse 中

java - Spring 中的 "BindException: Address already in use"与 Apache FtpServer

java - 使用 Spring 重试进行某些重试后,将有毒消息推送到回退队列 [IBM MQ]

javascript - JQuery Accordion 与树结构冲突

javascript - XUL。 Tree.为什么子项会掉落?

JavaServlet : Unable to create Excel File

java - Spring Boot、Hibernate 启动两次

algorithm - 当一个节点消失时如何组织MST?

java - 如何从firebase获取动态Url