我有一个Criteria-Query,它连接第二个表B,从表A中选择实体。问题是,这个查询多次从表A返回一些实体。但我需要结果是截然不同的。
使用Criteria.DISTINCT_ROOT_ENTITY是没用的,因为这会在执行SQL-Query后过滤掉多次出现。因此,当我将结果限制为20次点击时,我最终只有4次,尽管有更多条目符合我的查询。
在纯SQL中,我只需在查询中添加“GROUP BY ID”,一切都很好,因为只使用表B的连接,从表A中选择实体。但是使用Criteria-API我不能做这个。添加“GROUP BY”的唯一方法是使用Projections。但是,我最后得到的是标量值,而不是我班级的真实实例。使用SQL限制也不起作用,因为hibernate在我的“GROUP BY”-clause之后添加了一个含糊的“1 = 1”。 :(
有什么想法吗?
答案 0 :(得分:2)
你有没有试过这样的东西?
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 :(得分:1)
没有投射的群组:在你可能找到的许多答案中,这是不可能的,但是大多数人都不想使用投影,因为它需要他们投射每一个和每个属性,但要求是必须投射一个bean。 (并返回结果)。在下面的示例中,我尝试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,我希望它是唯一的,一种方法是在Set中获取结果列表,但我不喜欢这个方式原因很多:)
所以我从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;
}
}
答案 2 :(得分:0)
可以编写Hibernate可用于返回实体的实际SQL查询。因此,如果您确实需要,可以绕过HQL并使用GROUP BY准确编写所需的查询。
有关详细信息,请参阅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。}简写语法
答案 3 :(得分:0)
在没有预测的情况下进行分组的主要问题是,在某些DBMS(如Oracle)中它不起作用,Oracle将返回错误。
如果您对选择进行分组,则必须按所选的所有非聚合字段进行分组。例如,MySQL没有这个限制。
我一直在使用的方法是只选择id作为groupProperty投影,其中包含所有过滤器,排序和结果数限制。然后使用这些检索到的id执行其他查询过滤。这样,实现将独立于DBMS。
答案 4 :(得分:0)
我使用的解决方法是将sqlRestriction(“ TRUE GROUP BY this_.some_field”)添加为最后一个条件构建器调用。查询生成器将不会发出任何警告,而只会将其放在WHERE子句的末尾,该子句将最终显示为“其中a = 1和b = 2 ....并由this_.some_field进行真正分组” 不要忘记使用“ this_”前缀按根实体字段进行分组,否则,您可以启动应用程序,并查看查询(如果要使用分组的联接表属性)中别名的命名方式。
这种方法有助于解决由于联接而导致的n + 1行查询问题,并将完全重构延迟到可用时间为止。
在Hibernate 3.4 + MySQL 5.6上进行了测试