Как получить отличные результаты в hibernate с объединениями и ограничениями на основе строк (подкачка)?



Я пытаюсь реализовать подкачку с помощью ограничения на основе строк (например:setFirstResult(5) и setMaxResults(10)) в запросе критериев гибернации, который имеет соединения с другими таблицами.



понятно, что данные отсекаются случайным образом; и причина этого объясняется здесь.



в качестве решения на странице предлагается использовать "второй sql select" вместо соединения.



как я могу преобразовать мой существующий запрос критериев (который имеет соединения с помощью createAlias()) для использования a вложенный выбор вместо этого?

822   10  

10 ответов:

вы можете достичь желаемого результата, запросив список различных идентификаторов вместо списка различных гидратированных объектов.

просто добавьте это к вашим критериям:

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

теперь вы получите правильное количество результатов в соответствии с вашим ограничением на основе строк. Причина, по которой это работает, заключается в том, что проекция будет выполнять проверку отчетливости в рамках sql-запрос, вместо того, что делает ResultTransformer, который должен фильтровать результаты для отчетливость после SQL-запрос был выполнен.

стоит отметить, что вместо получения списка объектов, теперь вы получите список идентификаторов, которые можно использовать для гидратации объектов из спящего режима позже.

Я использую этот с моими кодами.

просто добавьте это к вашим критериям:

критерии.setResultTransformer (критерии.DISTINCT_ROOT_ENTITY);

этот код будет похож на select distinct * из таблицы собственного sql. Надеюсь, это поможет.

небольшое обустройство на предложение FishBoy по.

можно сделать такой запрос в один удар, а не в два отдельных этапа. т. е. один запрос ниже будет страница различные результаты правильно, а также возвращает сущности вместо просто идентификаторы.

просто используйте DetachedCriteria с проекцией id в качестве подзапроса, а затем добавьте значения подкачки в объект main 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, упомянутое в этой статье
  • сохраняет порядок и не использует предложение in на возможно большом наборе данных PK

Полная статья может быть найдена на мой блог

Hibernate дает возможность определить метод выборки ассоциаций не только во время разработки, но и во время выполнения путем выполнения запроса. Поэтому мы используем этот подход в сочетании с простым материалом 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();

это отличается от определения FetchMode ваших объектов во время разработки. Таким образом, вы можете использовать обычную ассоциацию соединения, извлекающую алгоритмы подкачки в вашем пользовательском интерфейсе, потому что это в большинстве случаев не критическая часть, и более важно, чтобы ваши результаты были как можно быстрее.

Ниже приведен способ, которым мы можем сделать несколько проекций для выполнения различных

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

где различие делается в памяти, после загрузки объектов!

Comments

    Ничего не найдено.