package com.ankurm.hibernatedemo.aggregate; import static org.assertj.core.api.Assertions.assertThat; import com.ankurm.hibernatedemo.HibernateDemoApplication; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Root; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * Proves the article's aggregate-function claims against a real H2 database: what an aggregate * query returns over an empty table, {@code select new} record construction under GROUP BY / * HAVING, the Criteria API equivalent, and HQL's {@code row_number()} window function. * *
Docs: docs/21-aggregate-functions.md.
*/
@SpringBootTest(classes = HibernateDemoApplication.class)
class AggregateFunctionsTest {
@Autowired
private EntityManagerFactory emf;
@Test
void emptyResultSet_countIsZero_sumAndAvgAreNull_neitherThrows() {
// Scoped to a category that matches zero rows -- rather than relying on the whole table
// being empty, which would make this test's outcome depend on execution order against
// the other test methods in this class that share the same EntityManagerFactory/database.
String noSuchCategory = "no-such-category-zzz";
EntityManager em = emf.createEntityManager();
Long count = em.createQuery(
"select count(p) from Product p where p.category = :c", Long.class)
.setParameter("c", noSuchCategory)
.getSingleResult();
Double sum = em.createQuery(
"select sum(p.price) from Product p where p.category = :c", Double.class)
.setParameter("c", noSuchCategory)
.getSingleResult();
Double avg = em.createQuery(
"select avg(p.price) from Product p where p.category = :c", Double.class)
.setParameter("c", noSuchCategory)
.getSingleResult();
em.close();
System.out.println("RESULT[aggregate-empty-result-set]: over 0 matching rows -- "
+ "count(p)=" + count + " (never null) | sum(p.price)=" + sum + " | avg(p.price)="
+ avg + " -- getSingleResult() returned normally for all three, no "
+ "NoResultException, because SQL's aggregate functions over zero rows still "
+ "produce exactly one result row.");
assertThat(count).isZero();
assertThat(sum).isNull();
assertThat(avg).isNull();
}
@Test
void groupByHaving_selectNewRecord_producesTypedSummaries() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(new Product("Keyboards", "Compact 65%", 89.0, 40));
em.persist(new Product("Keyboards", "Full-size", 59.0, 15));
em.persist(new Product("Keyboards", "Ergonomic split", 149.0, 5));
em.persist(new Product("Monitors", "27in 4K", 399.0, 8));
em.getTransaction().commit();
// Scoped to this test's own two categories -- the entity manager factory's H2 database is
// shared across every test method in this class, so an unscoped query here would also
// pick up Cables/Mice from the other test methods and make this assertion order-dependent.
List