Java 将分组依据添加到Hibernate条件查询中而不进行投影

Java 将分组依据添加到Hibernate条件查询中而不进行投影,java,hibernate,group-by,criteria,Java,Hibernate,Group By,Criteria,我有一个连接第二个表B的条件查询,用于从表a中选择实体。问题是,此查询多次返回表a中的某些实体。但我需要的结果是明确的 使用Criteria.DISTINCT\u ROOT\u实体是无用的,因为这会过滤掉执行SQL查询后出现的多个事件。因此,当我将结果限制为20次点击时,我只得到4次,尽管有更多的条目与我的查询匹配 在纯SQL中,我只需在查询中添加一个“GROUPBYID”,一切都很好,因为表B的连接仅用于从表a中选择实体。但使用CriteriaAPI,我无法做到这一点。添加“分组依据”的唯一方

我有一个连接第二个表B的条件查询,用于从表a中选择实体。问题是,此查询多次返回表a中的某些实体。但我需要的结果是明确的

使用Criteria.DISTINCT\u ROOT\u实体是无用的,因为这会过滤掉执行SQL查询后出现的多个事件。因此,当我将结果限制为20次点击时,我只得到4次,尽管有更多的条目与我的查询匹配

在纯SQL中,我只需在查询中添加一个“GROUPBYID”,一切都很好,因为表B的连接仅用于从表a中选择实体。但使用CriteriaAPI,我无法做到这一点。添加“分组依据”的唯一方法是使用投影。但是,我最终得到的是标量值,而不是类的真实实例。使用SQL限制也不起作用,因为hibernate在我的“GROUPBY”-子句之后添加了一个虚假的“1=1”:(


有什么想法吗?

可以编写实际的SQL查询,Hibernate可以使用这些查询返回实体。因此,如果确实需要,您可以绕过HQL,在其中编写您想要的查询

有关详细信息,请参阅

例如,您可以在hbm.xml文件中定义类似以下内容的查询:

<sql-query name="exampleQuery">
<query-param name="param" type="int"/>
<return alias="x" class="foo.bar.X"/>
<return alias="y" class="foo.bar.Y"/>
    <![CDATA[
        select {x.*}, {y.*}
        from XEntity x
        inner join YEntity y on y.xId = x.id
        where y.value = :param
    ]]>
</sql-query>


注意{x.}和{y.}简写语法,用于选择实体x和实体y的所有属性

您是否尝试过使用类似的方法

    ICriteria criteria = dc.GetExecutableCriteria(RepositoryInstance.Session)
            .SetProjection(Projections.distinct(Projections.projectionList()
                    .add(Projections.property("Prop1"), "Prop1")
                    .add(Projections.property("Prop2"), "Prop2")
                    .add(Projections.property("Prop3"), "Prop3")
                    .add(Projections.property("Prop4"), "Prop4")));
    result = criteria.List();
您可以通过类的反射动态添加属性

这将创建如下SQl:
从类中选择不同的prop1、prop2、prop3、prop4


我没有包括
DetachedCriteria dc
,因为这是不相关的。

无投影分组:这是不可能的,因为在许多答案中,您可能会发现它是有意义的,但大多数人不想使用投影,因为它要求他们投影每个属性,但要求bean必须是proje在下面的示例中,我尝试将所需的
bean
作为结果对象
project

我相信我用了一点小技巧也得到了同样的结果,首先我尝试在没有投影的情况下应用group by,但我没有找到解决方案,所以我不得不依赖投影

这就是我想要实现的目标

select p.* FROM parent p INNER JOIN child c ON p.id_parent=c.id_father
WHERE c.child_name like '%?%' AND p.parent_name like '%?%' 
group by p.id_parent
在Java代码中,我希望
p.*
成为一个
Parent
类,它是我的实体bean,我希望它是唯一的,一种方法是在一个集合中获取结果列表,但我不喜欢这种方法,原因很多:)

因此,我从
Child.class
而不是
Parent.class
创建了一个条件,这个技巧对我很有效

Criteria c = session.createCriteria(Child.class,"c");// starting from Child
    c.add(Restrictions.like("childName",   "abc", MatchMode.ANYWHERE));
    c.createAlias("parent", "p"); //remember parent is an attribute in Child.class
    c.add(Restrictions.like("p.parentName",   "xyz", MatchMode.ANYWHERE));
    c.setProjection( Projections.projectionList().add(Projections.groupProperty("parent"))); //projecting parent which is an attribute of Child.class

    List<Parent> result = c.list(); //get the result
    for (Parent p: result) {
        System.out.println(p);
    }

按分组而不按您希望的方式进行投影的主要问题是,在某些DBMS(如Oracle)中,它将不起作用,Oracle将返回一个错误

如果对select进行分组,则必须按所选的所有非聚合字段进行分组。例如MySQL没有这个限制


我一直使用的方法是只选择id作为groupProperty投影,所有过滤器、排序和结果数都是有限的。然后使用这些检索到的ID执行其他查询过滤。这样,实现将独立于DBMS。

我使用的解决方法是添加sqlRestriction(“TRUE GROUP BY this.some\u字段”)作为最后一个criteria builder调用。查询生成器不会放置任何警告,只会将其放在WHERE子句的末尾,该子句将以“WHERE a=1和b=2…和true group by this_u.some_字段”结尾 请不要忘记使用“this_u2;”前缀按根实体字段分组,或者如果这是要用于分组的联接表属性,则可以启动应用程序并查看别名在查询中的命名方式

这种方法有助于解决由于连接而导致的查询的n+1行问题,并将完全重构延迟到可用的时间


根据定义,在Hibernate 3.4+MySQL 5.6上测试GROUP BY(分组依据),涉及数据的聚合,这就是为什么需要进行预测。也许如果您添加了更多表的细节,以及希望Hibernate生成的SQL查询,我们可以给出更好的建议。这个类有一个日期列表。(实际上,这些日期是一个特殊的类DateWrapper,它包装日期并添加Id,因为hibernate目前无法加入值类型的集合)。我想查询事件,并查找所有事件,其中一个或多个事件在x和y之间。当我绘制由CriteriaAPI生成的SQL查询并添加一个“GROUPBYID”时,它正好完成了我要查找的内容。但是我找不到任何方法来说服Hibernate加入这个组!谢谢你的回答:)但是我没有使用HQL。我使用的是CriteriaAPI,而且我一定要使用它,因为查询必须动态编译!因此,我需要找到一种方法,告诉Hibernate按id属性对结果进行分组,而不使用投影,因为我需要类的真实实例作为reslut,不是标量值。也可以通过编程方式指定本机SQL查询,而不是如上所示的命名查询-尽管不是通过使用criteria API。这是唯一合法的方法,但这样更改查询可能会导致添加不需要的联接或花费额外的时间重构使用方法。此外,我们可以通过id获取根实例的投影属性,并通过提供id列表进行get()调用。但这些都是解决方法,因为criteria builder中缺少将根实体作为property()投影加载到结果映射中的方法。
package com.mazhar.beans;

import static javax.persistence.GenerationType.IDENTITY;

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "parent")
public class Parent {
    private Integer idParent;
    private String parentName;
    private List<Child> childs;

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id_parent")
    public Integer getIdParent() {
        return idParent;
    }
    public void setIdParent(Integer idParent) {
        this.idParent = idParent;
    }

    @Column(name = "parent_name")
    public String getParentName() {
        return parentName;
    }
    public void setParentName(String parentName) {
        this.parentName = parentName;
    }

    @OneToMany(fetch=FetchType.LAZY, mappedBy="parent", cascade=CascadeType.ALL)
    public List<Child> getChilds() {
        return childs;
    }
    public void setChilds(List<Child> childs) {
        this.childs = childs;
    }

}
package com.mazhar.beans;

import static javax.persistence.GenerationType.IDENTITY;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "child")
public class Child {
    private Integer idChild;
    private String childName;
    private Parent parent; //this actually we projected in criteria query.

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id_city", unique = true, nullable = false)
    public Integer getIdChild() {
        return idChild;
    }

    public void setIdChild(Integer idChild) {
        this.idChild = idChild;
    }

    @Column(name = "city_name", nullable = false)
    public String getChildName() {
        return childName;
    }

    public void setChildName(String cName) {
        this.childName = cName;
    }

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "id_father")
    public Parent getParent() {
        return parent;
    }

    public void setParent(Parent parent) {
        this.parent = parent;
    }
}