如何创建JPA标准查询以将给定集合中的每个项目与ElementCollection进行匹配

问题描述

首先,我创建了一个通用谓词,试图在元素集合中查找任何匹配项。例如。实体:

@Entity
public class Movie {
...
    @ElementCollection
    private Set<String> genre;
...
}

谓词将与包含[“ action”,“ drama”]所有类型类型中具有“ action”或“ drama”的电影的给定集合匹配。

谓词正在查找以下内容(泛型):

public static <E> Predicate matchStringSetCaseInsensitive(Set<String> set,SetAttribute<E,String> attribute,CriteriaBuilder criteriaBuilder,Root<E> root,boolean isstrict) {
    Predicate predicate;
    if (!isEmpty(set)) {
        List<Predicate> matchEach = new ArrayList<>();
        Join<E,String> joinedString = root.join(attribute,JoinType.LEFT);
        for (String string : set) {
            matchEach.add(criteriaBuilder.equal(criteriaBuilder.lower(joinedString),string.toLowerCase()));
        }
        if (isstrict) {
            //NOT working 
            predicate = criteriaBuilder.and(matchEach.toArray(new Predicate[] {}));
        } else {
            predicate = criteriaBuilder.or(matchEach.toArray(new Predicate[] {}));
        }
    } else {
        predicate = criteriaBuilder.isEmpty(root.get(attribute));
    }
    return predicate;
}
在上述情况下,

isstrict设置为false

问题是,它不能与isstrict = true一起使用。如果为true,查询将尝试查找具有与所有给定流派相匹配的流派的条目-这显然行不通。给定“动作”,“戏剧”,它将匹配流派等于“动作”和“戏剧”的条目。

如何进行这项工作?我考虑过使用or并计算不同的结果:

  • isstrict = true意味着Givenset.size =某些子字符串的计数
  • isstrict = false表示1

有人可以提供建议以针对目标实体中的ElementCollection创建匹配集合中每个给定项目的谓词吗?

解决方法

我设法创建了一个单独的谓词来匹配给定集合中的所有元素:

public static <E> Predicate matchStringSetContainsAll(Set<String> set,SetAttribute<E,String> attribute,CriteriaBuilder cB,Root<E> root,CriteriaQuery<E> query,boolean strict) {
        Predicate predicate;

        if (!isEmpty(set)) {
            Subquery<Long> subquery = query.subquery(Long.class);
            Root<E> subQueryRoot = subquery.correlate(root);

            SetJoin<E,String> tagJoin = subQueryRoot.join(attribute);
            Predicate[] match = set.stream().map(tag -> cB.equal(cB.lower(tagJoin),tag.toLowerCase()))
                    .toArray(Predicate[]::new);
            Predicate tagMatch = cB.or(match);

            subquery.select(cB.count(tagJoin)).where(tagMatch);
            Predicate matchAll = cB.equal(subquery,set.size());
            Predicate strictP = cB.and();
            if (strict) {
                Subquery<Long> countAll = query.subquery(Long.class);
                Root<E> countAllRoot = countAll.correlate(root);
                SetJoin<E,String> join = countAllRoot.join(attribute);
                countAll.select(cB.count(join));
                strictP = cB.equal(countAll,set.size());
            }
            predicate = cB.and(matchAll,strictP);
        } else {
            predicate = cB.isEmpty(root.get(attribute));
        }

        return predicate;
    }

如果strict==true仅在目标属性的条目数不超过给定值时匹配。

方法是在此处建议的子选择中使用count: Finding items with a set containing all elements of a given set with jpql

(第二个答案两次否定in无效)