programing

조인 및 행 기반 제한 (페이징)을 사용하여 최대 절전 모드에서 고유 한 결과를 얻는 방법은 무엇입니까?

nasanasas 2020. 11. 3. 08:06
반응형

조인 및 행 기반 제한 (페이징)을 사용하여 최대 절전 모드에서 고유 한 결과를 얻는 방법은 무엇입니까?


다른 테이블에 대한 조인이있는 Hibernate Criteria 쿼리에서 행 기반 제한 (예 : setFirstResult(5)and setMaxResults(10))을 사용하여 페이징을 구현하려고합니다 .

당연히 데이터가 무작위로 잘리고 있습니다. 그 이유는 여기 에 설명되어 있습니다 .

해결책으로 페이지는 조인 대신 "두 번째 SQL 선택"을 사용하도록 제안합니다.

createAlias()대신 중첩 선택을 사용하도록 기존 기준 쿼리 ( 를 사용하는 조인이 있음 )를 변환하려면 어떻게 해야합니까?


고유 한 수화 된 개체 목록 대신 고유 ID 목록을 요청하여 원하는 결과를 얻을 수 있습니다.

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

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

이제 행 기반 제한에 따라 올바른 수의 결과를 얻을 수 있습니다. 이것이 작동하는 이유는 프로젝션이 SQL 쿼리가 수행 된 결과를 구별하기 위해 결과를 필터링하는 ResultTransformer가 수행하는 작업 대신 SQL 쿼리의 일부로 구별 검사 수행하기 때문 입니다.

주목할만한 점은 객체 목록을 가져 오는 대신 나중에 최대 절전 모드에서 객체를 수화하는 데 사용할 수있는 ID 목록을 얻게된다는 것입니다.


이 코드를 내 코드와 함께 사용하고 있습니다.

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

criteria.setResultTransformer (Criteria.DISTINCT_ROOT_ENTITY);

해당 코드는 네이티브 SQL의 테이블에서 select distinct *와 같습니다. 이것이 도움이되기를 바랍니다.


FishBoy의 제안에 따라 약간의 개선이 이루어졌습니다.

두 개의 개별 단계가 아닌 한 번의 조회로 이러한 종류의 쿼리를 수행 할 수 있습니다. 즉, 아래의 단일 쿼리는 고유 한 결과를 올바르게 페이지로 표시하고 ID 대신 항목을 반환합니다.

ID 프로젝션이있는 DetachedCriteria를 하위 쿼리로 사용하고 기본 Criteria 개체에 페이징 값을 추가하기 만하면됩니다.

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

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

아주 잘 작동합니다.


ORDER BY를 사용하려면 다음을 추가하십시오.

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

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

이 솔루션은 다음과 같은 장점이 있습니다.

  • 이 기사에서 언급 한 PK id 솔루션보다 빠름
  • Ordering을 유지하고 PK의 큰 데이터 세트에 'in 절'을 사용하지 않습니다.

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

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를 SELECT로 변경하도록 기준 개체를 조언 할 수 있습니다.

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

이를 수행하는 것은 디자인 타임에 엔티티의 FetchMode를 정의하는 것과 다릅니다. 따라서 UI의 페이징 알고리즘에 대한 일반 조인 연결 페칭을 사용할 수 있습니다. 이는 대부분의 경우 중요한 부분이 아니고 가능한 한 빨리 결과를 얻는 것이 더 중요하기 때문입니다.


다음은 Distinct를 수행하기 위해 다중 투영을 수행 할 수있는 방법입니다.

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

}

ExtraProjections.java

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

개체를로드 한 후 메모리에서 구분이 수행됩니다!

참고URL : https://stackoverflow.com/questions/300491/how-to-get-distinct-results-in-hibernate-with-joins-and-row-based-limiting-pagi

반응형