Как получить отличные результаты в спящем режиме с объединениями и ограничениями на основе строк (пейджинг)?
Я пытаюсь реализовать пейджинг с использованием ограничения по строкам (например: setFirstResult(5)
и setMaxResults(10)
) в запросе критерия гибернации, который имеет соединения с другими таблицами.
Понятно, что данные обрезаются случайным образом; и причина этого объясняется здесь.
В качестве решения на странице предлагается использовать "второй sql select" вместо соединения.
Как я могу преобразовать свой существующий запрос критериев (который имеет объединения с помощью createAlias()
) вместо использования вложенного select?
Ответы
Ответ 1
Вы можете достичь желаемого результата, запросив список различных идентификаторов вместо списка отдельных гидратированных объектов.
Просто добавьте это по вашим критериям:
criteria.setProjection(Projections.distinct(Projections.property("id")));
Теперь вы получите правильное количество результатов в соответствии с вашим ограничителем на основе строк. Причина этого в том, что проекция будет выполнять проверку четности как часть запроса sql, вместо того, что делает ResultTransformer, который должен фильтровать результаты для отличимости после sql-запрос был выполнен.
Следует отметить, что вместо получения списка объектов теперь вы получите список идентификаторов, которые вы можете использовать для гидратации объектов из спящего режима позже.
Ответ 2
Я использую этот код с моими кодами.
Просто добавьте это по вашим критериям:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
этот код будет похож на выделенный отчет * из таблицы собственного sql. Надеюсь, что это поможет.
Ответ 3
Небольшое улучшение по предложению FishBoy.
Можно делать такой запрос в одном ударе, а не в двух отдельных этапах. то есть один запрос ниже будет правильно отображать различные результаты, а также возвращать объекты, а не только идентификаторы.
Просто используйте DetachedCriteria с проекцией id в качестве подзапроса, а затем добавьте значения поискового вызова в главный объект 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();
Ответ 4
Небольшое улучшение предложения @FishBoy заключается в использовании проекции id, поэтому вам не нужно жестко закодировать имя свойства идентификатора.
criteria.setProjection(Projections.distinct(Projections.id()));
Ответ 5
Решение:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
работает очень хорошо.
Ответ 6
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
Ответ 7
Теперь я объясню другое решение, в котором вы можете использовать обычный запрос и метод разбивки на страницы, не имея проблемы с возможными дубликатами или подавленными элементами.
У этого решения есть преимущество:
- быстрее, чем решение PK id, упомянутое в этой статье.
- сохраняет заказ и не использует параметр "in" на возможно большом наборе данных PK
Полную статью можно найти на мой блог
Hibernate дает возможность определять метод выборки ассоциации не только во время разработки, но и во время выполнения путем выполнения запроса. Таким образом, мы используем этот aproach в сочетании с простыми средствами перехвата и также можем автоматизировать процесс изменения алгоритма выборки свойств запроса только для свойств коллекции.
Сначала мы создаем метод, который разрешает все свойства коллекции из класса Entity:
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 ваших объектов во время разработки. Таким образом, вы можете использовать обычную ассоциативную привязку, использующую алгоритмы подкачки в пользовательском интерфейсе, потому что в большинстве случаев это не критическая часть, и более важно, чтобы ваши результаты были как можно быстрее.
Ответ 8
если вы хотите использовать ORDER BY, просто добавьте:
criteria.setProjection(
Projections.distinct(
Projections.projectionList()
.add(Projections.id())
.add(Projections.property("the property that you want to ordered by"))
)
);
Ответ 9
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);
где различие выполняется в памяти, после объектов загрузки!
Ответ 10
Ниже мы можем сделать несколько проекций для выполнения 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