我有一个具有很多字段,多对一和多对多关系的类,我需要按某些列(来自该类以及所连接的类的字段)添加动态过滤器,并按这些字段也是如此。
我在按多对多字段进行过滤和排序时遇到问题
@Entity
class EntityA {
...
@ManyToMany
@JoinTable (
name = "EntityA_EntityB",
joinColumns = { @JoinColumn(name = "EntityB") },
inverseJoinColumns = { @JoinColumn(name = "entityb_id") }
)
private List<EntityB> bEntities;
...
}
我有按EntityB.name过滤EntityA的规范(我设置了CriteriaQuery.distinct(true)来防止重复,而我没有这个)
public class EntityASpecifications {
//other specifications
...
public static Specification<EntityA> entityBNameContains(String query) {
return (root, criteriaQuery, criteriaBuilder) -> {
if (query == null) {
return criteriaBuilder.conjunction();
}
criteriaQuery.distinct(true);
return getContainsPredicate(criteriaBuilder, root.join("bEntities").get("name"), query);
};
}
private static Predicate getContainsPredicate(CriteriaBuilder criteriaBuilder, Expression<String> field, String query) {
return (query == null) ? criteriaBuilder.conjunction() : criteriaBuilder.like(criteriaBuilder.lower(field), getContainsPattern(query));
}
private static String getContainsPattern(String searchTerm) {
return (searchTerm.isEmpty()) ? "%" : "%" + searchTerm.toLowerCase() + "%";
}
}
它工作正常,问题是当我尝试同时使用排序和此过滤器时
entityARepository.findAll(EntityASpecifications.entityBNameContains("name"), PageRequest.of(page, size, Sort.Direction.ASC, sortColumnName));
对于作为EntityB.name连接到EntityA的字段,它也会失败(另外,我还有其他一些带有@ManyToOne的字段都失败了),并带有下一个异常:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: ORDER BY items must appear in the select list if SELECT DISTINCT is specified
如果我删除criteriaQuery.distinct(true);一切都会好起来的,但是我会重复,但我不想让它们
如何解决它,同时又没有重复的结果?
答案 0 :(得分:2)
获得EntityA重复结果的原因是您将EntityA与EntityB联接在一起,并且您的谓词基于EntityB中的文件。因此,如果有多个满足您条件的EntityB条目并且它们都属于同一个EntityA,则您将获得EntityA的多个条目。因此,解决方案是使用“存在”而不是将两个表连接在一起,并且您将不再需要使用distinct。您的规范如下所示:
public class EntityASpecifications {
//other specifications
...
public static Specification<EntityA> entityBNameContains(String query) {
return (root, criteriaQuery, criteriaBuilder) -> {
if (query == null) {
return criteriaBuilder.conjunction();
}
Subquery< EntityB> subquery = query.subquery(EntityB.class);
Root< EntityB> subqueryRoot = subquery.from(EntityB.class);
subquery.select(subqueryRoot);
subquery.where(criteriaBuilder.and(criteriaBuilder.equal(root, subqueryRoot.get("entitya_id")),
criteriaBuilder.like(criteriaBuilder.lower("name"), getContainsPattern(query)))
);
return criteriaBuilder.exists(subquery);
};
}
private static String getContainsPattern(String searchTerm) {
return (searchTerm.isEmpty()) ? "%" : "%" + searchTerm.toLowerCase() + "%";
}
}