Projection.count () и Projection.countDistinct () приводят к одному и тому же запросу - PullRequest
2 голосов
/ 23 февраля 2010

РЕДАКТИРОВАТЬ: я полностью отредактировал этот пост, так что новое описание моей проблемы включает в себя все детали, а не только то, что я ранее считал уместным. Возможно, это новое описание поможет решить проблему, с которой я столкнулся.

У меня есть два класса сущностей, 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 (
        ?, ?
    )

Запрос именно то, что я хотел, но с одним исключением. Поскольку Customer может принадлежать к нескольким группам CustomerGroup, оставленное присоединение к CustomerGroup приведет к дублированию объектов Customer. Следовательно, count(*) будет давать ложное значение, поскольку оно только подсчитывает, сколько результатов есть. Мне нужно получить количество уникальных клиентов, и этого я ожидал достичь с помощью Projections.countDistinct("uuid"); -проекции. По какой-то причине, как вы можете видеть, проекция все равно приведет к запросу count(*) вместо ожидаемого count(distinct uuid). Замена проекции countDistinct на count("uuid") приведет к точно такому же запросу.

Я что-то не так делаю или это ошибка?

===

«Проблема» решена. Причина: PEBKAC (проблема существует между клавиатурой и стулом). В моем коде была ветвь, и я не понимал, что ветвь была выполнена. Эта ветвь использовала rowCount () вместо countDistinct ().

Ответы [ 5 ]

4 голосов
/ 30 апреля 2010

Есть ошибка в 3.5.1

После

cr.setProjection (Projections.countDistinct ( "MemberID"));

Результатом SQL является count (memberId)

бо не в счет (отдельный 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 );
}
3 голосов
/ 24 февраля 2010

«Проблема» решена. Причина: PEBKAC (проблема существует между клавиатурой и стулом). В моем коде была ветвь, и я не понимал, что ветвь была выполнена. Эта ветвь использовала rowCount () вместо countDistinct ().

1 голос
/ 23 февраля 2010

попробуйте отладить метод toSqlString в классе CountProjection. Глядя на код, я могу только изобразить, что отличное не было правдой; Я не вижу другой причины, по которой отличное не будет включено:

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

С уважением,
Стейн

0 голосов
/ 23 февраля 2010

Может быть, идентификатор поля определяется как уникальный в любом случае? Попробуйте использовать другое поле, которое не является уникальным.

0 голосов
/ 23 февраля 2010

Полагаю, это потому, что ваше поле 'id' является уникальным, и Hibernate знает это. Дает ли запрос другой результат, когда вы используете «отдельный»?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...