简体   繁体   中英

Is it possible to add count(*) and groupBy to Spring Data Jpa Specification?

I'm writing an app using Spring Boot and Spring Data Jpa.

I have a method which uses RSQL (I use this library) and Specification to make a filter call with where clauses to the db.

My repository:

public interface BaseRepository<M extends BaseModel> extends JpaRepository<M, Long>, JpaSpecificationExecutor<M>{

}

In the service I make a call to the repository:

String filter = "createdDate > 2019-09-04T11:52:59.449";
return repository.findAll(toSpecification(filter), pageable).getContent();  

So I use the library above to generate a Specification and pass it to the repository.

Now I need to group a result by some fields.

The question how to add count and groupBy to a specification? Or maybe I need to create a predicate where I define this count and groupBy (somehow) and make a specification out of this? What is a better way to do it?

UPDATE #0

I've managed to add a group by section to the result sql query. I create a new specification object where I define all the group by parameters and then combine this specification with the original one.

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

                return query.getGroupRestriction();
            }
        };


return repository.findAll(groupBySpec.and(toSpecification(filter)), pageable).getContent();     

The thing is it always selects all the columns but I need to select only those which are in the group by section.

As a result I get a sql exception: ERROR: column "columnName" must appear in the GROUP BY clause or be used in an aggregate function .

I've tried to use multiselect like this criteriaQuery.multiselect(root.get("column1"), root.get("column2")).distinct(true).getGroupRestriction(); but it selects all the columns anyway.

It works if I use @Query("query") where I define which columns to select but I need it to work using Criteria Api/Specification.

You can create your own GroupBySpecification class which adds aa groupBy expression to an other specification:

public class GroupBySpecification implements Specification {
  Specification original;
  Parameters parameters; // whatever data needed for calculating groupBy expression

  public GroupBySpecification(Specification original, Parameters parameters){
      this.original = original;
      this.parameters = parameters;
  }
  public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {

      Expression<?> expression = ...; // whatever logic needed to be used on parameters...
      criteriaBuilder.groupBy(expression);
      return original.toPredicate(root, query, criteriabuilder);
  }

}

Then you can use it like this:

String filter = "createdDate > 2019-09-04T11:52:59.449";
return repository.findAll(new GroupBySpecification(toSpecification(filter),parameters),
   pageable).getContent();  

if you need count then call the appropriate repository method:

repository.count(new GroupBySpecification(toSpecification(filter),parameters));  

This cannot be done. Specifications are supposed to an abstraction only on the WHERE clause. It's similar to QueryDSLPredicateExecutor. The better approach here might be to use queryDSL or criteriaAPI to create the dynamic query you want to create. You can use a combination of the two to work it out.

Check - https://github.com/spring-projects/spring-data-jpa/issues/2361#issuecomment-986922491

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM