0

I have 2 entities like that:

@Entity
@Table(name = "registry_groups")
public class RegistryGroupEntity {
    @Id
    @SequenceGenerator(name="registry_groups_gen", sequenceName="registry_groups_id_seq", allocationSize = 1)
    @GeneratedValue(generator="registry_groups_gen")
    private Integer id;
    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "registryGroup")
    private Collection<ServiceEntity> services;
//setters-getters ommited
}

@Entity
@Table(name = "services")
public class ServiceEntity {
    @Id
    @SequenceGenerator(name="service_gen", sequenceName="services_id_seq", allocationSize = 1)
    @GeneratedValue(generator="service_gen")
    private Integer id;
    @Column(nullable = false, length = 100)
    private String name;

    @Column(name = "registry_group_id", insertable = false, updatable = false)
    private Integer registryGroupId;

}

And I try to make a counter function based on the second entity joined to the firs one.

The first approach was:

public long getRegistryGroupSimpleServicesIntCount(String name) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Long> c = cb.createQuery(Long.class);
        Root<RegistryGroupEntity> registryGroup = c.from(RegistryGroupEntity.class);

        Join<RegistryGroupEntity, ServiceEntity> serviceJoin = registryGroup.join(RegistryGroupEntity_.services, JoinType.INNER);
        //

        //Subquery<String> concatSubQuery = c.subquery(String.class);
        //Root<RegistryGroupEntity> countSubQueryRoot = concatSubQuery.from(RegistryGroupEntity.class);
        //concatSubQuery.select(serviceJoin.get(ServiceEntity_.name));
        //https://stackoverflow.com/questions/19601948/must-appear-in-the-group-by-clause-or-be-used-in-an-aggregate-function

        c.select(cb.count(serviceJoin.getSelection()));
        c.orderBy(cb.desc(serviceJoin.get(ServiceEntity_.name)));

        List<Predicate> criteria = new ArrayList<>();
        Map<String, Object> paramValues = new HashMap<>();
        if (name != null) {
            ParameterExpression<String> p = cb.parameter(String.class, "name");
            criteria.add(cb.like(serviceJoin.get(ServiceEntity_.name), p));
            paramValues.put("name", name);
        }
        if (criteria.size() == 1) {
            c.where(criteria.get(0));
        } else if (criteria.size() > 0) {
            c.where(cb.and(criteria.toArray(new Predicate[0])));
        }

        TypedQuery<Long> q = entityManager.createQuery(c);

        for (String paramName : paramValues.keySet()) {
            q.setParameter(paramName, paramValues.get(paramName));
        }

        return q.getSingleResult();
    }

Generate HQL is:

select count(services1_.id) as col_0_0_ from registry_groups registrygr0_ inner join services services1_ on registrygr0_.id=services1_.registry_group_id order by services1_.name desc

and the error message is:

ERROR: column "services1_.name" must appear in the GROUP BY clause or be used in an aggregate function

however, as you see, there is no group by clause in the query.

So, with enabling the subquery I have tried another approach:

public long getRegistryGroupSimpleServicesIntCount(String name) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery c = cb.createQuery(Long.class); Root registryGroup = c.from(RegistryGroupEntity.class);

    Join<RegistryGroupEntity, ServiceEntity> serviceJoin = registryGroup.join(RegistryGroupEntity_.services, JoinType.INNER);
    //здесь мы хотим получить все возможные услуги для поиска в фильре

    Subquery<String> concatSubQuery = c.subquery(String.class); //here the new part starts
    Root<RegistryGroupEntity> countSubQueryRoot = concatSubQuery.from(RegistryGroupEntity.class);
    concatSubQuery.select(serviceJoin.get(ServiceEntity_.name));
    //https://stackoverflow.com/questions/19601948/must-appear-in-the-group-by-clause-or-be-used-in-an-aggregate-function

    c.select(cb.count(countSubQueryRoot)); //here ends
    c.orderBy(cb.desc(serviceJoin.get(ServiceEntity_.name)));

    List<Predicate> criteria = new ArrayList<>();
    Map<String, Object> paramValues = new HashMap<>();
    if (name != null) {
        ParameterExpression<String> p = cb.parameter(String.class, "name");
        criteria.add(cb.like(serviceJoin.get(ServiceEntity_.name), p));
        paramValues.put("name", name);
    }
    if (criteria.size() == 1) {
        c.where(criteria.get(0));
    } else if (criteria.size() > 0) {
        c.where(cb.and(criteria.toArray(new Predicate[0])));
    }

    TypedQuery<Long> q = entityManager.createQuery(c);

    for (String paramName : paramValues.keySet()) {
        q.setParameter(paramName, paramValues.get(paramName));
    }

    return q.getSingleResult();
}

But now I'm getting another error with the followng reason in the stacktrace:

Caused by: org.postgresql.util.PSQLException: ERROR: column "generatedalias0" does not exist
  Position: 14

and the generated HQL is:

select count(generatedAlias0) from RegistryGroupEntity as generatedAlias1 inner join generatedAlias1.services as generatedAlias2 order by generatedAlias2.name desc

How to work it around with still trying to get the data from the join?

Eljah
  • 4,188
  • 4
  • 41
  • 85
  • 1
    what is your goal? to get total row count in resultset using single query? – Andrey B. Panfilov Apr 13 '23 at 21:24
  • It was so. I was able to get the same count with work around, based on suing ServiceEntity as the root, but now I'd like to fugure out, why the HQL is generated with the error, which part makes in inconsistent. – Eljah Apr 14 '23 at 04:47

0 Answers0