조인 및 행 기반 제한 (페이징)으로 최대 절전 모드에서 뚜렷한 결과를 얻는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/300491

문제

행 기반 제한을 사용하여 페이징을 구현하려고합니다 (예 : 예 : : setFirstResult(5) 그리고 setMaxResults(10)) 다른 테이블에 결합 된 최대 절단 기준 쿼리.

당연히 데이터는 무작위로 차단되고 있습니다. 그리고 그 이유는 설명됩니다 여기.

솔루션 으로이 페이지는 조인 대신 "두 번째 SQL Select"를 사용하는 것을 제안합니다.

기존 기준 쿼리를 어떻게 변환 할 수 있습니까? createAlias()) 대신 중첩 선택을 사용하려면?

도움이 되었습니까?

해결책

별도의 수화 된 객체 목록 대신 별도의 ID 목록을 요청하여 원하는 결과를 얻을 수 있습니다.

기준에 이것을 추가하기 만하면됩니다.

criteria.setProjection(Projections.distinct(Projections.property("id")));

이제 행 기반 제한에 따라 올바른 결과를 얻을 수 있습니다. 이것이 작동하는 이유는 투영이 별개의 점검을 수행하기 때문입니다. 의 일부로 SQL 쿼리는 결과적 ~ 후에 SQL 쿼리가 수행되었습니다.

주목할만한 가치는 객체 목록을 얻는 대신 이제 ID 목록을 얻게되며 나중에 최대 절전 모드에서 객체를 수화시키는 데 사용할 수 있습니다.

다른 팁

나는 이것을 내 코드와 함께 사용하고 있습니다.

기준에 이것을 추가하기 만하면됩니다.

Criteria.SetResultTransformer (Criteria.DistInct_Root_entity);

이 코드는 네이티브 SQL 테이블에서 선택된 별개 *와 같습니다. 이것이 도움이되기를 바랍니다.

Fishboy의 제안에 대한 약간의 개선 건물.

두 단계가 아닌 한 번의 히트로 이런 종류의 쿼리를 수행 할 수 있습니다. 즉 아래의 단일 쿼리는 별개의 결과를 올바르게 페이지에 올리며 ID 대신 엔티티를 반환합니다.

ID 프로젝션이 서브 쿼리로 분리 기업을 사용한 다음 기본 기준 객체에 페이징 값을 추가하십시오.

다음과 같이 보일 것입니다.

DetachedCriteria idsOnlyCriteria = DetachedCriteria.forClass(MyClass.class);
//add other joins and query params here
idsOnlyCriteria.setProjection(Projections.distinct(Projections.id()));

Criteria criteria = getSession().createCriteria(myClass);
criteria.add(Subqueries.propertyIn("id", idsOnlyCriteria));
criteria.setFirstResult(0).setMaxResults(50);
return criteria.list();

@fishboy의 제안을 약간 개선하는 것은 ID 프로젝션을 사용하는 것이므로 식별자 속성 이름을 하드 코딩 할 필요가 없습니다.

criteria.setProjection(Projections.distinct(Projections.id()));
session = (Session) getEntityManager().getDelegate();
Criteria criteria = session.createCriteria(ComputedProdDaily.class);
ProjectionList projList = Projections.projectionList();
projList.add(Projections.property("user.id"), "userid");
projList.add(Projections.property("loanState"), "state");
criteria.setProjection(Projections.distinct(projList));
criteria.add(Restrictions.isNotNull("this.loanState"));
criteria.setResultTransformer(Transformers.aliasToBean(UserStateTransformer.class));

이것은 나를 도왔습니다 : d

해결책:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

아주 잘 작동합니다.

주문을 사용하려면 다음을 추가하십시오.

criteria.setProjection(
    Projections.distinct(
        Projections.projectionList()
        .add(Projections.id())
        .add(Projections.property("the property that you want to ordered by"))
    )
);

이제 다른 솔루션을 설명하겠습니다. 여기서 다른 솔루션은 복제 또는 억제 된 항목의 문제없이 일반 쿼리 및 페이지 매김 방법을 사용할 수 있습니다.

이 솔루션은 다음과 같은 발전이 있습니다.

  • 이 기사에서 언급 한 PK ID 솔루션보다 빠릅니다.
  • 순서를 보존하고 PK의 큰 데이터 세트에서 'in clause'를 사용하지 않습니다.

전체 기사는 찾을 수 있습니다 내 블로그

Hibernate는 설계 시간뿐만 아니라 쿼리 실행에 의해 런타임에도 협회 페치 방법을 정의 할 수 있습니다. 따라서 우리는이 aproach를 간단한 Relfection 물건과 함께 사용하고 수집 속성에 대해서만 쿼리 속성 페치 알고리즘을 변경하는 프로세스를 자동화 할 수 있습니다.

먼저 엔티티 클래스에서 모든 수집 속성을 해결하는 메소드를 만듭니다.

public static List<String> resolveCollectionProperties(Class<?> type) {
  List<String> ret = new ArrayList<String>();
  try {
   BeanInfo beanInfo = Introspector.getBeanInfo(type);
   for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
     if (Collection.class.isAssignableFrom(pd.getPropertyType()))
     ret.add(pd.getName());
   }
  } catch (IntrospectionException e) {
    e.printStackTrace();
  }
  return ret;
}

이를 수행 한 후이 작은 도우미 방법을 사용하면 기준 객체를 조언하여 해당 쿼리에서 선택하도록 FetchMode를 변경하십시오.

Criteria criteria = …

//    … add your expression here  …

// set fetchmode for every Collection Property to SELECT
for (String property : ReflectUtil.resolveCollectionProperties(YourEntity.class)) {
  criteria.setFetchMode(property, org.hibernate.FetchMode.SELECT);
}
criteria.setFirstResult(firstResult);
criteria.setMaxResults(maxResults);
criteria.list();

이를 수행하는 것은 설계 시간에 엔터티의 페치 모드를 정의하는 것과 다릅니다. 따라서 UI의 페이징 알고리즘에 대한 일반적인 조인 협회 페치를 사용할 수 있습니다. 대부분의 시간은 중요한 부분이 아니며 가능한 한 빨리 결과를 얻는 것이 더 중요하기 때문입니다.

아래는 우리가 별개의 수행을 위해 여러 가지 투영을 수행 할 수있는 방법입니다.

    package org.hibernate.criterion;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.type.Type;

/**
* A count for style :  count (distinct (a || b || c))
*/
public class MultipleCountProjection extends AggregateProjection {

   private boolean distinct;

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

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

   public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery) 
   throws HibernateException {
      return new Type[] { Hibernate.INTEGER };
   }

   public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery) 
   throws HibernateException {
      StringBuffer buf = new StringBuffer();
      buf.append("count(");
      if (distinct) buf.append("distinct ");
        String[] properties = propertyName.split(";");
        for (int i = 0; i < properties.length; i++) {
           buf.append( criteriaQuery.getColumn(criteria, properties[i]) );
             if(i != properties.length - 1) 
                buf.append(" || ");
        }
        buf.append(") as y");
        buf.append(position);
        buf.append('_');
        return buf.toString();
   }

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

}

외부 주사. 자바

package org.hibernate.criterion; 

public final class ExtraProjections
{ 
    public static MultipleCountProjection countMultipleDistinct(String propertyNames) {
        return new MultipleCountProjection(propertyNames).setDistinct();
    }
}

샘플 사용 :

String propertyNames = "titleName;titleDescr;titleVersion"

criteria countCriteria = ....

countCriteria.setProjection(ExtraProjections.countMultipleDistinct(propertyNames);

참조 https://forum.hibernate.org/viewtopic.php?t=964506

NullPointerException 일부 경우에! 없이 criteria.setProjection(Projections.distinct(Projections.property("id")))모든 쿼리가 잘됩니다! 이 솔루션은 나쁘다!

또 다른 방법은 sqlquery를 사용하는 것입니다. 제 경우에는 다음 코드가 잘 작동합니다.

List result = getSession().createSQLQuery(
"SELECT distinct u.id as usrId, b.currentBillingAccountType as oldUser_type,"
+ " r.accountTypeWhenRegister as newUser_type, count(r.accountTypeWhenRegister) as numOfRegUsers"
+ " FROM recommendations r, users u, billing_accounts b WHERE "
+ " r.user_fk = u.id and"
+ " b.user_fk = u.id and"
+ " r.activated = true and"
+ " r.audit_CD > :monthAgo and"
+ " r.bonusExceeded is null and"
+ " group by u.id, r.accountTypeWhenRegister")
.addScalar("usrId", Hibernate.LONG)
.addScalar("oldUser_type", Hibernate.INTEGER)
.addScalar("newUser_type", Hibernate.INTEGER)
.addScalar("numOfRegUsers", Hibernate.BIG_INTEGER)
.setParameter("monthAgo", monthAgo)
.setMaxResults(20)
.list();

구별은 데이터베이스에서 이루어집니다! 반대로 :

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

로드 엔터티 후에는 구별이 메모리에서 수행되는 곳!

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top