2010-01-09 94 views
5

我有一個標準的查詢,它連接第二臺B,選擇從表A.實體的問題是,這個查詢從表中的多返回一些實體倍。但我需要的結果是不同的。通過向Hibernate的標準查詢添加組沒有投影

使用Criteria.DISTINCT_ROOT_ENTITY是沒用的,是因爲...執行的SQL查詢後,該過濾掉多次出現。所以,當我將結果限制爲20次時,我最終只有4個,儘管有更多條目符合我的查詢。

在純SQL中,我可以在查詢中添加一個「GROUP BY ID」,因爲表B的連接僅用於從表A中選擇實體,但是使用Criteria-API I不能這樣做。添加「GROUP BY」的唯一方法是使用Projections。但是,我最終得到的是標量值,而不是我的班的真實實例。使用SQL限制也不起作用,因爲hibernate在我的「GROUP BY」條款後添加了一個「1 = 1」。 :(

任何想法?

+0

「GROUP BY」根據定義,涉及數據的彙總,這就是投影必要的原因。也許如果你添加了表格的更多細節,以及你希望Hibernate生成的SQL查詢,我們可以提供更好的建議。 – skaffman

+0

我有一個類的事件。這個類有一個日期列表。 (實際上這些日期是一個特殊的DateWrapper類,它封裝了Date並添加了一個Id,因爲hibernate現在不能加入值類型的集合)。 我想查詢事件並查找x和y之間的一個或多個事件的所有事件。 當我抓取由Criteria-API生成的SQL-Query並添加一個「GROUP BY id」時,它確實,我正在尋找。但我找不到任何方式來爭論Hibernate加入GROUP BY! –

回答

0

它可以寫一個Hibernate可以使用返回實體實際的SQL查詢。所以,如果你真的需要,可以繞過HQL和寫入正是你想要與你的小組查詢通過它

詳見here

例如你可以在你的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

+0

感謝您的回答:)但我沒有使用HQL。我正在使用Criteria-API,並且我將與之綁定,因爲查詢必須動態編譯! 因此,我需要找到一種方法來告訴Hibernate通過id屬性對結果進行分組,而不使用Projections,因爲我需要將我的類的實例作爲reslut而不是標量值。 –

+0

也可以通過編程方式指定本地SQL查詢,而不是像上面所示的命名查詢 - 儘管不是使用標準API。 – alasdairg

1

簡寫語法您是否嘗試過使用這樣的事情?

 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:select distinct prop1,prop2,prop3,prop4 from yourClass

我不包括DetachedCriteria dc自認爲是無關緊要的。

1

GROUP BY無預測:它是不可能的,因爲它是有道理的,在許多答案中你可能會發現,但大多數人不想使用投影,因爲它要求他們投影每一個屬性,但要求是一個豆必須投影。 (並作爲結果返回)。在下面的例子中,我試圖以project所需的bean作爲結果對象。

我已經實現了相同的結果特技的一點點,我相信,首先我是想通過無投影應用組,但我沒有發現任何解決方案,所以我必須依靠投影。

這是我想達到

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); 
    } 

如果你還沒有想到這裏是我的映射實體Bean類。

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; 
    } 
} 
0

分組由不突出,在你想要的方式的主要問題是,在某些DBMS如Oracle,它不會工作,甲骨文將返回錯誤。

如果您對一個select進行分組,則必須按您選擇的所有非聚合字段進行分組。例如MySQL沒有這個限制。

我一直在使用的方法是隻選擇ID作爲groupProperty投影與所有過濾器,排序和結果數限制。然後使用這些檢索到的id來執行其他查詢過濾。這樣實現將獨立於DBMS。