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();
}
}
}
关键点解析:
✅ 分组操作:通过CriteriaQuery
的groupBy()
方法按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);
}
});
}
测试要点:
- 数据准备:在
@BeforeAll
方法中插入测试数据(电子产品2件,家具1件) - 结果验证:
- 确保返回2个分组结果
- 验证电子产品总价格(100+150=250)
- 验证家具总价格(200)
5. 总结
本文通过实际案例展示了如何使用Hibernate Criteria API实现分组查询,核心要点包括:
- 类型安全:Criteria API在编译时就能检查查询语法,避免运行时错误
- 程序化构建:通过Java代码动态构建查询,特别适合复杂业务场景
- 聚合函数支持:轻松实现sum、avg、count等聚合操作
这种方案相比原生SQL查询具有更好的可维护性和类型安全性,是处理动态分组需求的简单粗暴解决方案。