Criteria查询 --- 结果集Distinct解决方案1 / 2

本文深入探讨了Hibernate Criteria API中distinct查询的特点与应用技巧,重点介绍了对象List与List<Object[]>两种查询结果的不同处理方式,并给出了具体示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

相信大家都很喜欢Criteria查询,因为它对查询条件已经查询实体采用松散的组合,不像HQL那样需要拼字符串。
但是又会比较抵触它,因为对它的不熟悉,写复杂查询可能会不顺手,一个子查询也可能会很麻烦。

今天我用半天时间与大家分享Criteria查询里面弯路比较多的结果集distinct,这个在HQL里面很简单,但在Criteria查询还真是头疼,网上的文章也是很模糊而且讨论的比较片面,我希望通过这篇文章让大家少走弯路,多用Criteria查询,少用HQL拼字符串。呵呵。

Criteria的结果集最常用分两种:对象List、List<Object[]>(投影查询),这两种查询结果的distinct处理的方式也是不一样的。稍后会分别讨论。

本文用到模型类:

Device.java
====================================
@Entity
@Table(name = "DEVICE")
public class Device extends Maintainable {

private Long id;

private String name;

private NodeCabinet nodeCabinet;// 所属机柜

// setter getter method ...
}

[size=large][b]1. 基本知识:[/b][/size]

[b][size=medium]1.1 Criteria不支持distinct对象结果集吗?[/size][/b]
re: Criteria通过setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)方法对查询后结果进行排重,请注意,这种方式是先查询出可能重复的记录然后根据每个对象的equals方法进行排重,如果不需要分页,而且你的查询结果集很小的话,可以使用这种方式distinct结果集。
[color=red]warning: [/color]
[list]
[*]1. 要排重的对象要实现equals方法。
[*]2. 不能进行分页处理,因为是先查询后distinct,结果会不准确。
[*]3. 查询处理的结果集太大的话,可能存在性能问题,相同记录会查询多次。
[/list]

示例代码 1-1:
	Criteria c = manager.createCriteria(Device.class);
.....
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
List result = c.list();


[b][size=medium]1.2 Criteria如何进行投影查询我需要的几个字段?[/size][/b]
re:Criteria通过设置projectionList就可以只查询指定字段。
[color=red]warning:[/color]Criteria投影查询出来的结果集格式是这样的:List<Object[]> 前台遍历集合的时候需要使用数组下标访问数据。

示例代码 1-2:
	Criteria c = manager.createCriteria(Device.class);
.....
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.property("id"));
projectionList.add(Projections.property("name"));
c.setProjection(projectionList); //生成如下SQL: select id, name from ....
List result = c.list();



[size=large][b]2. Criteria投影distinct查询[/b][/size]

[size=medium][b]2.1 Criteria投影distinct查询[/b][/size]
Criteria对投影的支持比返回对象的方式支持的好很多。以下是样例代码,与比示例1-2代码只有一行差别:

示例代码 2-1:
	Criteria c = manager.createCriteria(Device.class);
.....
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.property("id"));
projectionList.add(Projections.property("name"));
c.setProjection(Projections.distinct(projectionList)); // >>>> 这里差别 生成如下SQL: select distinct id, name from ....
List result = c.list();


[size=medium][b]2.2 分页情况下Criteria查询如何计算total records count[/b][/size]
投影情况下如何计算distinct后分页total records count?
re:Criteria通过Projection计算count。先算count然后在投影。计算count的可以封装到单独查询方法里。

示例代码:
	Criteria c = manager.createCriteria(Device.class);
.....
criteria.setProjection(Projections.countDistinct("id")); // select count(distcint id) from ...
Integer totalCount = (Integer) criteria.uniqueResult();

ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.property("id"));
projectionList.add(Projections.property("name"));
c.setProjection(Projections.distinct(projectionList)); // 生成如下SQL: select distinct id, name from ....
List result = c.list();
### JPA One-to-Many 查询结果去重解决方案 当执行 JPA 的 `one-to-many` 关联查询时,可能会遇到由于关联表中的多个记录映射到同一个实体而导致的结果重复问题。为了有效处理这种情况并确保返回唯一的结果合,可以采用多种方法。 #### 方法一:使用 Distinct 关键字 通过在 JPQL 或 Criteria API 中加入 `DISTINCT` 关键字来消除冗余条目。这会指示数据库只返回不同的组合项,从而避免因多对端产生的重复数据[^1]。 ```java // 使用JPQL实现distinct关键字去除重复对象 String jpql = "SELECT DISTINCT p FROM ParentEntity p JOIN FETCH p.children"; List<ParentEntity> resultList = entityManager.createQuery(jpql, ParentEntity.class).getResultList(); ``` #### 方法二:设置 FetchMode 和 BatchSize 调整 Hibernate 的抓取策略 (`FetchType.LAZY`) 可减少不必要的加载操作;同时设定批量大小参数能够优化性能表现。适当配置这些属性有助于改善查询效率,并间接缓解由过度获取引发的数据膨胀现象。 ```xml <!-- 在实体类上定义fetch模式 --> @OneToMany(mappedBy="parent", fetch=FetchType.LAZY) private Set<ChildEntity> children; ``` #### 方法三:应用流式处理技术 Stream().distinct() 对于已经取得的 List 合,还可以利用 Java 8 提供的新特性——Stream 流来进行过滤操作。此方式适用于内存级别的数据清理工作,在某些场景下可能更为灵活便捷。 ```java // 对已有的list进行stream() distinct() Set<ParentEntity> uniqueResults = parentEntities.stream().collect(Collectors.toCollection(LinkedHashSet::new)); ``` 以上三种途径均可用于解决 JPA `one-to-many` 映射关系下的结果中存在相同实例的问题。具体选用哪种手段取决于实际应用场景和个人偏好等因素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值