Projections.count()和Projections.countDistinct()都会产生相同的查询

时间:2010-02-23 14:45:02

标签: java hibernate criteria

编辑:我已经完全编辑了这篇文章,因此我的问题的新描述包括所有细节,而不仅仅是我之前认为相关的内容。也许这个新的描述将有助于解决我面临的问题。

我有两个实体类,Customer和CustomerGroup。客户和客户群之间的关系是ManyToMany。客户组在Customer类中以下列方式注释。


@Entity
public class Customer {
  ...
  @ManyToMany(mappedBy = "customers", fetch = FetchType.LAZY)
  public Set<CustomerGroup> getCustomerGroups() {
    ...
  }
  ...
  public String getUuid() {
    return uuid;
  }
  ...
}

客户群组类中的客户参考以下列方式注释


@Entity
public class CustomerGroup {
  ...
  @ManyToMany
  public Set<Customer> getCustomers() {
    ...
  }

  ...

  public String getUuid() {
    return uuid;
  }
  ...
}

请注意,CustomerGroup和Customer类也都有一个UUID字段。 UUID是一个唯一的字符串(在数据模型中不强制唯一性,正如您所看到的,它被处理为任何其他普通字符串)。

我要做的是获取所有不属于任何客户群的客户或客户群是“有效群组”。客户组的有效性由有效UUID列表定义。

我创建了以下条件查询


Criteria criteria = getSession().createCriteria(Customer.class);
criteria.setProjection(Projections.countDistinct("uuid"));
criteria = criteria.createCriteria("customerGroups", "groups", Criteria.LEFT_JOIN);

List<String> uuids = getValidUUIDs();
Criterion criterion = Restrictions.isNull("groups.uuid");
if (uuids != null && uuids.size() > 0) {
  criterion = Restrictions.or(criterion, Restrictions.in(
      "groups.uuid", uuids));
}
criteria.add(criterion);

执行查询时,将产生以下SQL查询


select
    count(*) as y0_ 
from
    Customer this_ 
left outer join
    CustomerGroup_Customer customergr3_ 
    on this_.id=customergr3_.customers_id 
left outer join
    CustomerGroup groups1_ 
    on customergr3_.customerGroups_id=groups1_.id 
where
    groups1_.uuid is null 
    or groups1_.uuid in (
        ?, ?
    )

查询正是我想要的,但有一个例外。由于客户可以属于多个CustomerGroup,因此加入CustomerGroup将导致重复的Customer对象。因此,count(*)将给出错误值,因为它只计算有多少结果。我需要获得独特客户的数量,这是我希望通过Projections.countDistinct("uuid"); - 投影实现的。出于某种原因,正如您所看到的,投影仍然会导致count(*)查询而不是预期的count(distinct uuid)。仅使用countDistinct替换投影count("uuid")将导致完全相同的查询。

我做错了什么还是这个错误?

===

“问题”解决了。原因:PEBKAC(键盘和椅子之间存在问题)。我的代码中有一个分支,并没有意识到分支已执行。该分支使用rowCount()而不是countDistinct()。

5 个答案:

答案 0 :(得分:4)

这是3.5.1中的错误

之后

cr.setProjection(Projections.countDistinct( “MEMBERID”));

SQL结果是count(memberId)

bo not count(distinct memberId)

调试后

....

public final class Projections {

    public static CountProjection countDistinct(String propertyName) {
        return new CountProjection(propertyName).setDistinct();
    }
注意到

不同但未使用。

public class CountProjection extends AggregateProjection {
    private boolean distinct;

    protected CountProjection(String prop) {
        super("count", prop);
    }

    public String toString() {
        if ( distinct ) {
            return "distinct " + super.toString();
        }
        else {
            return super.toString();
        }
    }

    public CountProjection setDistinct() {
        distinct = true;
        return this;
    }
}

和...在AggregateProjection中 只使用getFunctionName()= functionName =“count”!!!

公共类AggregateProjection扩展了SimpleProjection {

protected AggregateProjection(String functionName, String propertyName) {
    this.functionName = functionName;
    this.propertyName = propertyName;
}

public String getFunctionName() {
    return functionName;
}

public String getPropertyName() {
    return propertyName;
}

    ...

public String toSqlString(Criteria criteria, int loc, CriteriaQuery criteriaQuery)
        throws HibernateException {
    final String functionFragment = getFunction( criteriaQuery ).render(
            buildFunctionParameterList( criteria, criteriaQuery ),
            criteriaQuery.getFactory()
    );
    return functionFragment + " as y" + loc + '_';
}

protected SQLFunction getFunction(CriteriaQuery criteriaQuery) {
    return getFunction( getFunctionName(), criteriaQuery );
}

答案 1 :(得分:3)

“问题”解决了。原因:PEBKAC(键盘和椅子之间存在问题)。我的代码中有一个分支,并没有意识到分支已执行。该分支使用rowCount()而不是countDistinct()。

答案 2 :(得分:1)

尝试在CountProjection类中调试方法toSqlString。看看代码,我只能想象那种截然不同的代码;我没有看到为什么不包括distinct的原因:

    public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery) 
throws HibernateException {
    StringBuffer buf = new StringBuffer();
    buf.append("count(");
    if (distinct) buf.append("distinct ");
    return buf.append( criteriaQuery.getColumn(criteria, propertyName) )
        .append(") as y")
        .append(position)
        .append('_')
        .toString();
}

的问候,
斯泰恩

答案 3 :(得分:0)

我想这是因为你的'id'字段是一个唯一的字段,而hibernate知道这一点。 当您使用'distinct'时,查询是否会给出不同的结果?

答案 4 :(得分:0)

也许字段ID被定义为唯一?尝试使用不同的字段,这不是唯一的。