1. 概述

Hibernate作为强大的ORM框架,让我们能更专注于Java对象操作而非SQL细节。其Criteria API提供了程序化构建数据库查询的能力,特别适合处理动态和复杂查询场景。

本文将深入探讨如何利用Hibernate的Criteria API实现分组操作(Group-By),通过实际代码示例展示具体实现方式。

2. 模型搭建

首先创建一个基础的Product实体类,包含category(分类)和price(价格)字段。我们的目标是按分类分组并计算每个分类的总价格:

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    private String category;
    
    private Double price;

    // Getters and setters omitted for brevity
}

3. 使用Criteria API实现分组

Criteria API提供了程序化构建复杂查询的能力,包括分组操作。下面实现一个方法:按category分组并计算每个分类的总价格:

public class ProductService {
    private SessionFactory sessionFactory;

    public ProductService() {
        sessionFactory = new Configuration().configure().buildSessionFactory();
    }

    public List<Object[]> getTotalPricePerCategory() {
        try (Session session = sessionFactory.openSession()) {
            CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();

            CriteriaQuery<Object[]> query = criteriaBuilder.createQuery(Object[].class);
            Root<Product> root = query.from(Product.class);

            query.multiselect(
              root.get("category"),
              criteriaBuilder.sum(root.get("price"))
            ).groupBy(root.get("category"));

            return session.createQuery(query).getResultList();
        }
    }
}

关键点解析: ✅ 分组操作:通过CriteriaQuerygroupBy()方法按category字段分组 ✅ 聚合计算:使用criteriaBuilder.sum()计算每个分类的总价格 ✅ 结果封装:返回Object[]列表,每个数组包含分类名称和总价格

生成的SQL语句如下:

select
    p.category   c,
    sum(p.price) s
from
    product p
group by
    c

⚠️ 踩坑提示:这里使用了try-with-resources语法自动关闭Session,避免资源泄漏问题

4. 单元测试

现在添加单元测试验证分组查询的正确性:

@BeforeAll
public void setup() {
    productService = new ProductService();

    // 插入测试数据
    try (Session session = productService.getSessionFactory().openSession()) {
        session.beginTransaction();

        Product product1 = new Product("Product1", "Electronics", 100.0);
        Product product2 = new Product("Product2", "Electronics", 150.0);
        Product product3 = new Product("Product3", "Furniture", 200.0);

        session.persist(product1);
        session.persist(product2);
        session.persist(product3);

        session.getTransaction().commit();
    }
}

@Test
public void whenGroupedByCategory_thenReturnTotalPrice() {
    List<Object[]> results = productService.getTotalPricePerCategory();

    Assertions.assertEquals(2, results.size());

    results.forEach(record -> {
        String category = (String) record[0];
        Double totalPrice = (Double) record[1];

        if ("Electronics".equals(category)) {
            Assertions.assertEquals(250.0, totalPrice);
        } else if ("Furniture".equals(category)) {
            Assertions.assertEquals(200.0, totalPrice);
        }
    });
}

测试要点:

  1. 数据准备:在@BeforeAll方法中插入测试数据(电子产品2件,家具1件)
  2. 结果验证
    • 确保返回2个分组结果
    • 验证电子产品总价格(100+150=250)
    • 验证家具总价格(200)

5. 总结

本文通过实际案例展示了如何使用Hibernate Criteria API实现分组查询,核心要点包括:

  • 类型安全:Criteria API在编译时就能检查查询语法,避免运行时错误
  • 程序化构建:通过Java代码动态构建查询,特别适合复杂业务场景
  • 聚合函数支持:轻松实现sum、avg、count等聚合操作

这种方案相比原生SQL查询具有更好的可维护性和类型安全性,是处理动态分组需求的简单粗暴解决方案。