using group by with specification in Spring data count method


Keywords:group  by 


Question: 

I want to implement a spring data method which check result of the following query

select count(1) from installation_requested_t requested 
where requested.installation_id in 
      (select installation_id from installation_requested_t where request_id=?) 
group by requested.installation_id
having count(1)>1;

For the project, it is mandatory to use specification with the spring data jpa. So there is my repository method call :

installationRequestedRepository.count(
                               InstallationSpecs
                               .existsInstallationRequestLinkedToAnotherEPR(movingEprId)
);

and my spec method:

public static Specification<InstallationRequested> existsInstallationRequestLinkedToAnotherEPR(final Long EPRId) {
    return new Specification<InstallationRequested>() {

        @Override
        public Predicate toPredicate(Root<InstallationRequested> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

            Subquery<Long> InstallationIdsLinkedToEPRsubquery = query.subquery(Long.class);

            InstallationIdsLinkedToEPRsubquery.select(root.get(InstallationRequested_.id))
                    .where(cb.equal(root.get(InstallationRequested_.environmentPermitRequestId), EPRId));

            Subquery<InstallationRequested> installationRequestForTheseInstallationIdsWhoAreLinkedToAnotherEPR =
                    query.subquery(InstallationRequested.class);

            installationRequestForTheseInstallationIdsWhoAreLinkedToAnotherEPR
                    .select(installationRequestForTheseInstallationIdsWhoAreLinkedToAnotherEPR
                            .from(InstallationRequested.class))
                    .where(root.get(InstallationRequested_.id).in(InstallationIdsLinkedToEPRsubquery))
                    .groupBy(root.get(InstallationRequested_.id))
                    .having(cb.greaterThan(cb.count(cb.literal(1)), cb.literal(1l)));

            return cb.exists(installationRequestForTheseInstallationIdsWhoAreLinkedToAnotherEPR);

        }
    };

}

And my problem is that everything doesn't execute well:

org.springframework.dao.InvalidDataAccessApiUsageException: 
org.hibernate.hql.internal.ast.QuerySyntaxException: 
unexpected token: where near line 1, column 333 
[select count(generatedAlias0) from be.question.domain.model.request.installation.InstallationRequested as generatedAlias0 
where exists 
    (select generatedAlias1 from be.question.domain.model.request.installation.InstallationRequested as generatedAlias1 
    where generatedAlias0.id in 
        (select generatedAlias0.id from  where generatedAlias0.environmentPermitRequestId=1072487L) 
    group by generatedAlias0.id having count(1)>1L)
]; 

Do you have an idea to make my spec work and another way to make the asked task ?


1 Answer: 

I found a solution to my problem. I fix the specification:

public static Specification<InstallationRequested> existsInstallationRequestLinkedToAnotherEPR(final Long EPRId) {
    return new Specification<InstallationRequested>() {

        @Override
        public Predicate toPredicate(Root<InstallationRequested> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

            // subquery to select installation ids
            Subquery<Long> InstallationIdsLinkedToEPRsubquery = query.subquery(Long.class);
            Root<InstallationRequested> selectIdSubQueryRoot =
                    InstallationIdsLinkedToEPRsubquery.from(InstallationRequested.class);
            InstallationIdsLinkedToEPRsubquery
                    .select(selectIdSubQueryRoot.get(InstallationRequested_.installation).get(Installation_.id))
                    .where(cb.equal(selectIdSubQueryRoot.get(InstallationRequested_.environmentPermitRequestId),
                            EPRId));

            // subquery to select count of installation request group by installation id
            Subquery<Long> installationRequestForTheseInstallationIdsWhoAreLinkedToAnotherEPR =
                    query.subquery(Long.class);
            Root<InstallationRequested> groupBySubQueryRoot =
                    installationRequestForTheseInstallationIdsWhoAreLinkedToAnotherEPR
                            .from(InstallationRequested.class);
            Path<Long> installationIdInGroupBySubQuery =
                    groupBySubQueryRoot.get(InstallationRequested_.installation).get(Installation_.id);

            installationRequestForTheseInstallationIdsWhoAreLinkedToAnotherEPR.select(cb.count(cb.literal(1)))
                    .where(installationIdInGroupBySubQuery.in(InstallationIdsLinkedToEPRsubquery))
                    .groupBy(installationIdInGroupBySubQuery)
                    .having(cb.greaterThan(cb.count(cb.literal(1)), cb.literal(1l)));

            // returning existing condition on result of the group by
            return cb.exists(installationRequestForTheseInstallationIdsWhoAreLinkedToAnotherEPR);

        }
    };

}

By the way, if someone as a better idea i would be happy to read it ;)