Я создал иерархическую структуру или древовидную структуру для сайта покупок с помощью 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
Категория 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);
}
}
я создаю базу данных из этих сущностей, используя spring.jpa.hibernate.ddl-auto = создать
Кажется, ответ в вопросе. В чем конкретный вопрос / проблема с кодом, который вы разместили?
код не создает структуру, когда я его использую, поэтому, когда я пытаюсь создать категории и связать их с продуктами.
Ну, вы не разместили код, пытаясь создать эту структуру, так чем мы можем помочь?
я добавил код
Нет, ты этого не сделал. Где находится код, создающий категорию «Электроника без родительского элемента», категорию «Игры» без родительского элемента, категорию «Домашнее аудио с электроникой в качестве родительского элемента» и т. д.?
Я пытаюсь создать код, который может создать такую структуру, и код, который я добавил, - это то, что я сделал до сих пор, и мне нужна помощь в переносе моего текущего кода на этап, где он создает такую структуру
Что заставляет вас думать, что вас еще нет, если вы даже не пробовали делать то, что хотите?
Я получаю эту ошибку, когда пытаюсь создать продукт, org.hibernate.TransientPropertyValueException: объект ссылается на несохраненный временный экземпляр - сохраните временный экземпляр перед сбросом
Итак, сделайте то, что написано в сообщении об ошибке. Если вам понадобится помощь, еще раз опубликуйте код.
вы были правы, все остается в порядке, однако проблема заключается в запросе дерева




Вы можете использовать рекурсивный запрос для выбора родителей данной категории.
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
С точки зрения SQL проверьте предложение
connect by prior.