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