Spring boot JPA Dynami c запросов, Спецификация <Product>- toPredicate (...), фильтр запросов / сортировка - PullRequest
0 голосов
/ 26 января 2020

Я пытаюсь настроить динамические 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

Любое предложение поможет, большое спасибо:)

...