我具有使用RSQL(我使用this库)和规范来对数据库的where子句进行过滤器调用的功能。
我查询的模型非常简单:它只包含一堆属性,与其他模型没有任何关系。
存储库(我使用 JpaSpecificationExecutor 的通知):
public interface BaseRepository<M extends BaseModel> extends JpaRepository<M,Long>,JpaSpecificationExecutor<M>{
}
因此,我以字符串的形式获取过滤条件(我在运行时获取它们),然后创建一个新的规范,该规范将用于生成带where子句的结果sql查询。
在服务中,我使用指定符调用存储库方法 findAll :
String filter = "createdDate > 2019-09-04T11:52:59.449";
return repository.findAll(toSpecification(filter),pageable).getcontent();
现在,我需要添加新功能,该功能允许按运行时获得的某些特定字段进行分组,因此我不知道确切将使用哪些字段。
因此,通过创建新的规范并将其与原始规范组合,我设法将分组按节添加到结果sql查询中:
String filter = "createdDate > 2019-09-04T11:52:59.449";
Specification<M> groupBySpec = new Specification<M>() {
@Override
public Predicate toPredicate(Root<M> root,CriteriaQuery<?> query,CriteriaBuilder criteriaBuilder) {
List<Expression<?>> groupByParams = new ArrayList<>();
groupByParams.add(root.get("param1"));
groupByParams.add(root.get("param2"));
query.groupBy(groupByParams);
// Here I try to select specific columns
query.multiselect(root.get("column1"),root.get("column2")).distinct(true);
return query.getGroupRestriction();
}
};
return repository.findAll(groupBySpec.and(toSpecification(filter)),pageable).getcontent();
但是我得到了一个SQL异常ERROR: column "columnName" must appear in the GROUP BY clause or be used in an aggregate function
。
此操作的结果,因为它实际上选择了所有列,而忽略了我的query.multiselect
。
我尝试仅使用Criteria Api而不使用JPA规范来实现它,例如:
public List<Object[]> getallWithSpecific(){
CriteriaBuilder cb = em.getcriteriaBuilder();
CriteriaQuery<Object[]> q = cb.createQuery(Object[].class);
Root<Model> c = q.from(Model.class);
List<Expression<?>> groupByParams = new ArrayList<>();
groupByParams.add(c.get("column1"));
groupByParams.add(c.get("column2"));
q.groupBy(groupByParams);
q.select(cb.array(c.get("column1"),c.get("column2"),cb.count(q.from(Model.class))));
return em.createQuery(q).getResultList();
}
它确实有效,它只是选择了我需要的东西。但是,我不能仅以这种方式使用它,因为我需要将此 group by 功能与我先前介绍的过滤器,分页功能结合使用。
由于JpaRepository按照给定模型的条件工作,所以我不知道如何对它说“只抓住我需要的列,而不是整个模型”
我还尝试向jpa存储库中添加一个用@Query("actual jpql query")
注释的方法,我可以在其中描述选择的内容:
@Query("select m.column1,m.column2,count(*) from Model m group by m.column1,m.column2")
List<Object[]> customMethod();
它也起作用。因此,现在它了解到即使我仍然可以在同一模型的条件下使用它,我也只需要抓住特定的列即可。
我也了解 projections ,它实际上是一个接口,我可以在其中定义列以选择和使用它,例如:List<Projection> findAll()
。
但这不是我的情况,不如使用@Query
,因为我在运行时检索要选择的组的列,所以它是动态的,而不是静态的。
总结:可以使用Jpa规范选择特定的列,而无需添加描述选择内容的新类(这就是@Query
的工作原理,它只是从jpql查询中获取所有内容,而无需任何其他类\接口,也可以)。也许我需要重写jpa repo的方法findAll
以返回List<Object[]>
,就像它与@Query
和纯Criteria Api一起使用一样?