Как получить отличные результаты в hibernate с объединениями и ограничениями на основе строк (подкачка)?
Я пытаюсь реализовать подкачку с помощью ограничения на основе строк (например:setFirstResult(5) и setMaxResults(10)) в запросе критериев гибернации, который имеет соединения с другими таблицами.
понятно, что данные отсекаются случайным образом; и причина этого объясняется здесь.
в качестве решения на странице предлагается использовать "второй sql select" вместо соединения.
как я могу преобразовать мой существующий запрос критериев (который имеет соединения с помощью createAlias()) для использования a вложенный выбор вместо этого?
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
Если вы хотите использовать 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