2010-02-23 1 views
2

편집 :이 게시물을 완전히 편집하여 문제의 새로운 설명에 이전에 관련이 있다고 생각했던 것뿐만 아니라 모든 세부 정보가 포함되도록했습니다. . 어쩌면이 새로운 설명은 제가 직면하고있는 문제를 해결하는 데 도움이 될 것입니다.Projections.count()와 Projections.countDistinct() 모두 동일한 쿼리 결과가 발생합니다.

두 개의 엔티티 클래스 인 Customer 및 CustomerGroup이 있습니다. 고객 그룹과 고객 그룹 간의 관계는 ManyToMany입니다. 고객 그룹은 고객 클래스에서 다음과 같은 방식으로 주석이 작성됩니다.

 

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

고객 그룹 클래스의 고객 참조는 다음과 같은 방법으로 CustomerGroup 및 고객 클래스 모두는 또한 UUID 필드를 가지고

 

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

    ... 

    public String getUuid() { 
    return 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에 가입하면 고객 오브젝트가 중복됩니다. 따라서 count(*)은 얼마나 많은 결과가 있는지 계산하기 때문에 잘못된 값을 제공합니다. 나는 유일한 고객의 양을 얻고 나는 이것을 Projections.countDistinct("uuid"); 투영을 사용하여 달성 할 것으로 예상했다. 어떤 이유로 인해 볼 수 있듯이 프로젝션은 여전히 ​​ count(distinct uuid) 대신 count(*) 쿼리가 발생합니다. 프로젝션 countDistinctcount("uuid")으로 바꾸면 정확히 동일한 쿼리가됩니다.

내가 잘못했거나 버그입니까?

===

"문제"가 해결되었습니다. 이유 : PEBKAC (키보드와 의자 사이에 문제가 있음). 내 코드에 분기가 있었고 분기가 실행되었음을 알지 못했습니다. 해당 분기는 countDistinct() 대신 rowCount()를 사용합니다.

+0

을 확장합니다. 조건을 사용하여 기본 쿼리를 작성한 후 수동으로 왼쪽 조인을 수동으로 추가하는 경우 : 그 방법은 아닙니다. – Fortega

+0

문제점과 관련된 모든 코드를 포함하도록 질문을 편집했습니다. –

+0

같은 문제를 겪고있는 다른 사람들은 다음 질문을 참조하십시오. http://stackoverflow.com/questions/4616607/projections-countdistinct-with-hibernate-produces-unexpected-result – simon

답변

3

"문제"해결 : 나는 다른 별개의가 포함되지 왜 이유가 없다. 이유 : PEBKAC (키보드와 의자 사이에 문제가 있음). 내 코드에 분기가 있었고 분기가 실행되었음을 알지 못했습니다. 해당 분기는 countDistinct() 대신 rowCount()를 사용합니다.

0

'id'필드가 고유 한 필드이고 최대 절전 모드를 알고 있기 때문입니다. 'distinct'를 사용할 때 쿼리가 다른 결과를 제공합니까?

0

어쩌면 필드 ID가 고유 한 것으로 정의 되었습니까? 고유하지 않은 다른 필드를 사용해보십시오.

+0

다른 필드를 사용해 보았지만 여전히 카운트(*). 사실, Projections.countDistinct()에 대한 매개 변수로 무엇을 제공해도 상관 없습니다. 심지어 "foobar"가 동일한 쿼리를 생성합니다. –

0

-> 다음 답변은 정확하지 않습니다 ... 나는 잠시 동안 여기에 남겨 둘 것입니다 ...

당신은

1

클래스 CountProjection의 방법 toSqlString 디버깅 시도 (또는이 필요하지?)

criteria = criteria.setProjection(Projections.countDistinct("id")); 

대신

criteria.setProjection(Projections.countDistinct("id")); 

으로해야한다. 코드를 보면 뚜렷한 것이 사실이 아니라는 것을 이미지 할 수 있습니다.

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

관련,
스테인

4

THRE는

cr.setProjection (Projections.countDistinct ("MEMBERID")) 후 3.5.1

버그이고;

SQL 결과가 카운트 (MEMBERID)

보 (별개의 MEMBERID)를 계산하지 디버그 후

.... 별개의

public final class Projections { 

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

눈치 만 사용되지 않습니다.

AggregateProjection 만 getFunctionName에
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; 
    } 
} 

와 ...는() = FUNCTIONNAME = "카운트"을 사용! 쿼리, 당신은 가입이 왼쪽을 지정하는 조건 코드의 나머지 부분을 제시해주십시오 조인 왼쪽 포함되어있는 경우

공용 클래스 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); 
} 
관련 문제