Я пытаюсь настроить динамические c запросы для моего сайта. Я хочу, чтобы пользователь мог фильтровать товары по цвету, происхождению, а также сортировать по цене. ASC / DES C.
URL моего API выглядит следующим образом: /product-types/main-category/{type}/{from}/{to}
, где
- type - это az enum (основная и подкатегория)
- от - до - параметры для разбиения на страницы
ProducController.java
@GetMapping("/product-types/main-category/{type}/{from}/{to}")
public Page<Product> getAllProductsByMainCat(
@PathVariable String type,
@PathVariable int from,
@PathVariable int to,
@RequestParam Optional<String> colour,
@RequestParam Optional<String> origin,
@RequestParam Optional<String> price
) {
MainProductType productType = MainProductType.valueOf(type.toUpperCase());
Pageable pageable = PageRequest.of(from, to, Sort.by("isInFrame").ascending().and(Sort.by("id").descending()));
return productService.getByDynamicQuery(productType, colour, origin, pageable);
}
Здесь я уже добавляю два параметра сортировки, один из которых isInFrame - просто логическое значение, а id , поэтому новейший продукт всегда на первом месте.
Но мне также нужно иметь возможность показывать продукты с выбранными цветами (цвет НРАВИТСЯ черный ИЛИ ИЛИ синий ИЛИ красный ... et c), и с выбранным происхождением. Источником является просто строка в таблице Product , но поскольку несколько продуктов могут иметь несколько цветов, а несколько цветов могут иметь несколько продуктов, то есть отношение ManyToMany и имеет собственную реляционную таблицу .
У меня есть ProductService
, здесь я получаю значения из Optionals
public class ProductService {
@Autowired
ProductRepository productRepo;
@Autowired
ColourRepository colourRepository;
public Page<Product> getByDynamicQuery(MainProductType productType, Optional<String> colour, Optional<String> origin, Pageable pageable) {
Colour colourValue = null;
String originValue = null;
if (colour.isPresent()) {
colourValue = colourRepository.getColourByName(colour.get());
}
if (origin.isPresent()) {
originValue = origin.get();
}
return productRepo.findAll(ProductSpecifications.withDynamicQuery(productType, colourValue, originValue), pageable);
}
}
, также здесь есть запрос, который используется в ProductService
:
@Query("SELECT c FROM Colour c WHERE lower(c.colorName) LIKE CONCAT('%',lower(:colourName),'%') ")
Colour getColourByName(String colourName);
И toPredicate (Root продукт, запрос CriteriaQuery, конструктор CriteriaBuilder) , что я пытаюсь использовать для добавления параметров к запросу, только если они существуют.
public static Specification<Product> withDynamicQuery(MainProductType productType, final Colour colour, final String origin) {
return new Specification<Product>() {
@Override
public Predicate toPredicate(Root<Product> product, CriteriaQuery<?> query, CriteriaBuilder builder) {
System.out.println(colour);
List<Predicate> predicates = new ArrayList<Predicate>();
if (colour != null) {
predicates.add(builder.and(builder.equal(product.get(Product_.colours), colour)));
}
if (origin != null) {
predicates.add(builder.and(builder.equal(product.get(Product_.origin), origin)));
}
predicates.add(builder.and(builder.equal(product.get(Product_.mainType), productType)));
Predicate[] predicatesArray = new Predicate[predicates.size()];
return builder.and(predicates.toArray(predicatesArray));
}
};
}
}
Фильтр источника работает нормально, но когда я пытаюсь запросить Color по colourName в ProductService, он выдает ошибку StackOverflowError, очевидно, потому что продукты и цвета находятся во многих отношениях.
Вот модели продуктов и цветов:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Double price;
private Integer availableQuantity;
private String URL;
private String width;
private String height;
private Boolean isInFrame;
private Boolean isBestSeller;
private String origin;
@Enumerated(EnumType.STRING)
private MainProductType mainType;
@Enumerated(EnumType.STRING)
private SubProductType subType;
@JsonIgnore
@ManyToMany
private List<ShoppingCart> shoppingCartList;
@JsonIgnore
@ManyToMany(fetch = FetchType.LAZY)
private List<Colour> colours;
@JsonIgnore
@OneToMany(mappedBy = "product")
private List<AdditionalProductImage> additionalProductImages;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
public class Colour {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String colorName;
@JsonIgnore
@ManyToMany(mappedBy = "colours", fetch = FetchType.LAZY)
private List<Product> products;
}
Итак, чтобы подвести итог - мне нужно сохранить два фильтра ( isInFrame, ID ) и нумерация страниц тоже , поэтому страница не загружает каждый продукт сразу, плюс мне нужно добавить больше фильтров - Color, Origin, Price ASC / DES C.
Мой вопрос: как я могу фильтровать , если у меня есть ManyToMany отношения между Продуктами и Цвета ... и куда мне поставить цену сортировку?
Вот картинка про интерфейс: https://imgur.com/UgJVITP
Любое предложение поможет, большое спасибо:)