You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate Criteria投影查询:如何分组并获取完整实体对象?

解决Hibernate Criteria查询中获取完整Entity及关联实体最大值的问题

我明白你的痛点——现在的查询只能拿到主键和最大值的数组,没法直接得到完整的Entity对象,还得保证是单次查询对吧?下面给你两种可行的方案,都是基于Hibernate Criteria实现的:

方案一:使用子查询+自定义DTO(推荐)

这种方法先通过子查询算出每个Entity对应的AnotherEntity.n的最大值,再和主查询关联,最后用结果转换器把完整Entity和最大值映射到一个自定义的DTO类里,既清晰又高效。

步骤1:定义DTO类

先创建一个简单的DTO来承载结果,它包含完整的Entity对象和最大值属性:

public class EntityWithMaxN {
    private Entity entity;
    private Integer maxN;

    // 无参构造器(Hibernate的AliasToBeanResultTransformer需要)
    public EntityWithMaxN() {}

    // 带参构造器和getter/setter
    public EntityWithMaxN(Entity entity, Integer maxN) {
        this.entity = entity;
        this.maxN = maxN;
    }

    // getter和setter方法
    public Entity getEntity() { return entity; }
    public void setEntity(Entity entity) { this.entity = entity; }
    public Integer getMaxN() { return maxN; }
    public void setMaxN(Integer maxN) { this.maxN = maxN; }
}

步骤2:构建Criteria查询

// 1. 构建子查询:计算每个Entity对应的AnotherEntity.n的最大值
DetachedCriteria maxNSubquery = DetachedCriteria.forClass(AnotherEntity.class, "ae")
        .setProjection(Projections.projectionList()
                .add(Projections.max("n")) // 取n的最大值
                .add(Projections.groupProperty("entity.pk"))) // 按Entity的主键分组
        .add(Restrictions.eqProperty("entity.pk", "e.pk")); // 关联主查询的Entity

// 2. 主查询:获取符合条件的完整Entity,同时关联子查询的最大值
Criteria c = session.createCriteria(Entity.class, "e");
c.add(Restrictions.eq("someEntityProperty", someEntityPropertyValue));

// 投影完整的Entity对象和子查询的最大值,并设置别名
ProjectionList projectionList = Projections.projectionList()
        .add(Projections.property("e"), "entity") // 投影整个Entity,别名为entity
        .add(Projections.subquery(maxNSubquery), "maxN"); // 投影子查询结果,别名为maxN

c.setProjection(projectionList);
// 使用结果转换器,把投影结果映射到DTO类
c.setResultTransformer(new AliasToBeanResultTransformer(EntityWithMaxN.class));

// 执行查询,得到包含完整Entity和最大值的DTO列表
List<EntityWithMaxN> resultList = c.list();

方案二:给Entity添加临时属性(适合不想创建DTO的场景)

如果不想额外定义DTO,可以给Entity类添加一个@Transient的临时属性来存储最大值,然后直接把结果映射回Entity对象:

步骤1:修改Entity类

添加一个临时属性,注意加上@Transient注解,避免Hibernate把它映射到数据库字段:

public class Entity {
    // 原有属性...
    private Long pk;
    private String someEntityProperty;
    private Set<AnotherEntity> anotherEntitySet;

    // 新增临时属性,存储AnotherEntity.n的最大值
    @Transient
    private Integer maxN;

    // 原有getter/setter...
    public Integer getMaxN() { return maxN; }
    public void setMaxN(Integer maxN) { this.maxN = maxN; }
}

步骤2:构建Criteria查询

// 子查询部分和方案一一致
DetachedCriteria maxNSubquery = DetachedCriteria.forClass(AnotherEntity.class, "ae")
        .setProjection(Projections.projectionList()
                .add(Projections.max("n"))
                .add(Projections.groupProperty("entity.pk")))
        .add(Restrictions.eqProperty("entity.pk", "e.pk"));

// 主查询:投影Entity的所有属性 + 最大值
Criteria c = session.createCriteria(Entity.class, "e");
c.add(Restrictions.eq("someEntityProperty", someEntityPropertyValue));

ProjectionList projectionList = Projections.projectionList();
// 投影Entity的所有字段(这里需要列出所有属性,比如pk、someEntityProperty等)
projectionList.add(Projections.property("pk"), "pk");
projectionList.add(Projections.property("someEntityProperty"), "someEntityProperty");
// ... 继续添加Entity的其他属性
projectionList.add(Projections.subquery(maxNSubquery), "maxN"); // 投影最大值到临时属性

c.setProjection(projectionList);
// 把结果映射回Entity对象
c.setResultTransformer(new AliasToBeanResultTransformer(Entity.class));

List<Entity> resultList = c.list();

⚠️ 注意:这种方法需要手动列出Entity的所有属性,当Entity字段较多时会比较繁琐,所以更推荐方案一。

为什么你的原代码不行?

原代码只投影了主键和最大值,没有包含Entity的其他属性,所以只能得到Object[]。要获取完整Entity,必须把Entity的所有属性(或整个对象)加入投影,再通过结果转换器把数据组装成对象。

内容的提问来源于stack exchange,提问作者Giulio Piancastelli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:53:03