Home > OS >  Spring JPA Specification filtering
Spring JPA Specification filtering

Time:09-22

I'm trying to filter parent entity by intersection of children property and inputed list. My entities:

@Entity
@Table(name = "recipes")
public class Recipe {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Ingredient> ingredientsList = new ArrayList<>();

    //other fields and methods
}
@Entity
@Table(name = "ingredients")
public class Ingredient {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "product")
    private String product;

    //other fields and methods
}

As result i want get list of Recipes. Each recipe must have all products from user's inputed list. I was trying smth like this:

public static Specification<Recipe> containsProducts(List<String> products) {
        return (root, query, criteriaBuilder) -> {
            if (Objects.isNull(products) || products.isEmpty()) {
                return criteriaBuilder.conjunction();
            }

            List<Predicate> predicates = new ArrayList<>();
            Join<Recipe, Ingredient> ingredientRecipeJoin = root.join("ingredientsList", JoinType.INNER);

            for (String product : products) {
                if (product.isBlank()) {
                    continue;
                }
                predicates.add(criteriaBuilder.equal(ingredientRecipeJoin.get(Ingredient_.PRODUCT), product));
            }

            return criteriaBuilder.and(predicates.toArray(Predicate[]::new));
        };
    }

But it works only when products list size=1.

CodePudding user response:

Finally i got answer. My miss is that i was trying to search recipes by only one join. For every item in user's inputed list i should create a new join and create on it a new prediction. Jpa logic prevent reusing columns from old joins. So my containsProducts method now looks like:

public static Specification<Recipe> containsProducts(List<String> products) {
        return (root, query, criteriaBuilder) -> {
            if (Objects.isNull(products) || products.isEmpty()) {
                return criteriaBuilder.conjunction();
            }

            List<Predicate> predicates = new ArrayList<>();
            Join<Recipe, Ingredient> ingredientRecipeJoin;

            for (String product : products) {
                if (product.isBlank()) {
                    continue;
                }
                ingredientRecipeJoin = root.join(Recipe_.INGREDIENTS_LIST, JoinType.INNER);
                predicates.add(criteriaBuilder.equal(ingredientRecipeJoin.get(Ingredient_.PRODUCT), product));
            }

            return criteriaBuilder.and(predicates.toArray(Predicate[]::new));
        };
    }
  • Related