Как запросить объект иерархического дерева категорий с неопределенной глубиной

Я создал иерархическую структуру или древовидную структуру для сайта покупок с помощью 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. findAllProducts в категории и
  2. найти все категории, связанные с продуктом.

Категория Entity

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

С точки зрения SQL проверьте предложение connect by prior.

Sid 03.11.2018 06:52

я создаю базу данных из этих сущностей, используя spring.jpa.hibernate.ddl-auto = создать

Erent 03.11.2018 06:54

Кажется, ответ в вопросе. В чем конкретный вопрос / проблема с кодом, который вы разместили?

JB Nizet 03.11.2018 09:16

код не создает структуру, когда я его использую, поэтому, когда я пытаюсь создать категории и связать их с продуктами.

Erent 03.11.2018 11:10

Ну, вы не разместили код, пытаясь создать эту структуру, так чем мы можем помочь?

JB Nizet 03.11.2018 19:18

я добавил код

Erent 03.11.2018 19:31

Нет, ты этого не сделал. Где находится код, создающий категорию «Электроника без родительского элемента», категорию «Игры» без родительского элемента, категорию «Домашнее аудио с электроникой в ​​качестве родительского элемента» и т. д.?

JB Nizet 03.11.2018 19:33

Я пытаюсь создать код, который может создать такую ​​структуру, и код, который я добавил, - это то, что я сделал до сих пор, и мне нужна помощь в переносе моего текущего кода на этап, где он создает такую ​​структуру

Erent 03.11.2018 19:57

Что заставляет вас думать, что вас еще нет, если вы даже не пробовали делать то, что хотите?

JB Nizet 03.11.2018 20:03

Я получаю эту ошибку, когда пытаюсь создать продукт, org.hibernate.TransientPropertyValueException: объект ссылается на несохраненный временный экземпляр - сохраните временный экземпляр перед сбросом

Erent 03.11.2018 20:22

Итак, сделайте то, что написано в сообщении об ошибке. Если вам понадобится помощь, еще раз опубликуйте код.

JB Nizet 03.11.2018 20:50

вы были правы, все остается в порядке, однако проблема заключается в запросе дерева

Erent 05.11.2018 04:09
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
11
12
3 850
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете использовать рекурсивный запрос для выбора родителей данной категории.

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. Возможно, вам потребуется изменить собственный запрос в соответствии с фактической базой данных, которую вы используете.

В PostgreSQL есть отличная документация о том, как это работает, см.

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

как мне изменить запрос для использования в postgres

Erent 09.11.2018 06:48

Другие вопросы по теме