# 21 — Aggregate functions [← Previous: 20 — Hibernate Validator CDI integration](20-hibernate-validator-cdi.md) | [Next: 22 — Sorting →](22-sorting.md) | [Back to README →](../README.md) Backs the rewrite of ankurm.com post 4888 (aggregate functions). ## What an aggregate query returns over zero rows `count`, `sum`, `avg`, `min`, `max` all still return exactly one result row when the underlying table (or the filtered subset) has zero matching rows — SQL's aggregate functions are defined over the whole result set, not per-row, so there is always one row to return. `count(*)` on an empty set is `0`; every other aggregate is `NULL`. Neither case throws `NoResultException` — `getSingleResult()` on an aggregate query only throws that when the query itself is malformed, never because the aggregate happened to be computed over nothing. ```java Long count = em.createQuery("select count(p) from Product p where p.category = :c", Long.class) .setParameter("c", "no-such-category-zzz").getSingleResult(); Double sum = em.createQuery("select sum(p.price) from Product p where p.category = :c", Double.class) .setParameter("c", "no-such-category-zzz").getSingleResult(); ``` [`AggregateFunctionsTest.java`](../src/test/java/com/ankurm/hibernatedemo/aggregate/AggregateFunctionsTest.java) — [captured output](output/21-empty-result-set.txt) ## `select new` with a Java record Hibernate 7 accepts a Java record's canonical constructor as a `select new` target exactly like it accepts a class constructor — no special annotation, no adapter, just a record whose constructor parameter types and order match the query's projection: ```java public record CategorySummary(String category, long productCount, double averagePrice) {} ``` [`CategorySummary.java`](../src/main/java/com/ankurm/hibernatedemo/aggregate/CategorySummary.java) ```sql select new com.ankurm.hibernatedemo.aggregate.CategorySummary(p.category, count(p), avg(p.price)) from Product p group by p.category having count(p) > 1 order by p.category ``` `HAVING count(p) > 1` filters on the *aggregated* group, after `GROUP BY` has collapsed the rows — a category with exactly one product is excluded by `HAVING`, not merely left ungrouped. [Test](../src/test/java/com/ankurm/hibernatedemo/aggregate/AggregateFunctionsTest.java) — [output](output/21-groupby-having-record.txt) ## The Criteria API equivalent `CriteriaBuilder.avg(...)`, `.sum(...)`, `.count(...)` and friends build the same aggregate SQL without a string query: ```java cq.multiselect(root.get("category"), cb.avg(root.get("price"))) .where(cb.equal(root.get("category"), "Cables")) .groupBy(root.get("category")); ``` [Test](../src/test/java/com/ankurm/hibernatedemo/aggregate/AggregateFunctionsTest.java) — [output](output/21-criteria-avg.txt) ## Window functions — not a Hibernate 7 feature HQL's `OVER` clause (`row_number()`, `rank()`, `lead()`, `lag()`, and the ordered-set / inverse distribution aggregates) is real and works exactly as SQL's window functions do. It is **not** new in Hibernate 7, though it is sometimes described that way: `javap` against `hibernate-core-7.4.5.Final.jar`'s `CommonFunctionFactory` shows a `windowFunctions()` registration method that has existed since Hibernate 6.2, when HQL's window-function support was first added. Hibernate 7 continues to support it; it did not introduce it. ```sql select p.name, p.price, row_number() over (partition by p.category order by p.price desc) from Product p where p.category = 'Mice' order by p.price desc ``` [Test](../src/test/java/com/ankurm/hibernatedemo/aggregate/AggregateFunctionsTest.java) — [output](output/21-window-row-number.txt) ## Going deeper - `getSingleResult()` vs `getResultList()` on an aggregate query: prefer `getSingleResult()` only when the query has no `GROUP BY` — a grouped aggregate can legitimately return many rows. - `StatelessSession` skips the persistence context entirely for a pure read-and-aggregate workload, avoiding the memory overhead of first-level cache entries the aggregate result itself never needs. - [Hibernate ORM Criteria API documentation](https://docs.jboss.org/hibernate/orm/7.4/userguide/html_single/Hibernate_User_Guide.html#criteria)