Jak uzyskać wyraźne wyniki w hibernacji z połączeniami i ograniczeniem opartym na wierszach (stronicowanie)?

Próbuję zaimplementować stronicowanie za pomocą ograniczenia opartego na wierszach (na przykład: setFirstResult(5) i setMaxResults(10)) na kwerendzie kryteriów Hibernate, która ma połączenia do innych tabel.

Zrozumiałe jest, że dane są wycinane losowo, a powód tego jest wyjaśniony tutaj .

Jako rozwiązanie strona sugeruje użycie" second SQL select " zamiast join.

Jak mogę przekonwertować moje istniejące zapytanie kryteria (które ma joins za pomocą createAlias()), aby zamiast tego użyć zagnieżdżonego select?

Author: Subodh Joshi, 2008-11-19

10 answers

Można osiągnąć pożądany rezultat, żądając listy odrębnych identyfikatorów zamiast listy odrębnych uwodnionych obiektów.

Po prostu dodaj to do swoich kryteriów:

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

Teraz otrzymasz poprawną liczbę wyników zgodnie z ograniczeniem opartym na wierszu. Powodem, dla którego to działa, jest to, że projekcja wykona sprawdzenie odróżnienia jako część zapytania sql, zamiast tego, co robi ResultTransformer, czyli filtrowanie wyników dla odróżnienia po zostało wykonane zapytanie sql.

Warto zauważyć, że zamiast otrzymywać listę obiektów, otrzymasz teraz listę identyfikatorów, których możesz użyć do nawodnienia obiektów z hibernacji później.

 101
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-19 00:53:36

Używam tego z moimi kodami.

Po prostu dodaj to do swoich kryteriów:

Kryteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

Ten kod będzie podobny do select distinct * from tabeli natywnego sql. Mam nadzieję, że to pomoże.

 43
Author: grayshop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-03 06:56:52

Mała poprawa oparta na sugestii Fishboya.

Możliwe jest wykonanie tego rodzaju zapytania w jednym trafieniu, a nie w dwóch oddzielnych etapach. tj. pojedyncze zapytanie poniżej wyświetli poprawnie odrębne wyniki, a także zwróci encje zamiast tylko identyfikatorów.

Po prostu użyj odłączonej criterii z projekcją id jako zapytania podrzędnego, a następnie dodaj wartości stronicowania do głównego obiektu kryteriów.

Będzie wyglądać mniej więcej tak:

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();
 24
Author: Daniel Alexiuc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-27 04:43:57

Małą poprawką do sugestii @ FishBoy jest użycie projekcji id, więc nie musisz kodować nazwy właściwości identyfikatora.

criteria.setProjection(Projections.distinct(Projections.id()));
 6
Author: nikita,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-08 15:04:32
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));

To mi pomogło: D

 4
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-24 13:12:50

Rozwiązanie:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
Działa bardzo dobrze.
 4
Author: JJ.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-16 10:52:34

Jeśli chcesz użyć ORDER BY, po prostu dodaj:

criteria.setProjection(
    Projections.distinct(
        Projections.projectionList()
        .add(Projections.id())
        .add(Projections.property("the property that you want to ordered by"))
    )
);
 2
Author: rekinyz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-14 16:01:59

Wyjaśnię teraz inne rozwiązanie, w którym można użyć zwykłej metody kwerendy i paginacji bez problemu z ewentualnymi duplikatami lub tłumionymi elementami.

To rozwiązanie ma przewagę, że jest:

  • szybciej niż rozwiązanie PK id wymienione w tym artykule
  • zachowuje kolejność i nie używa klauzuli " in " na potencjalnie dużym zbiorze danych PK

Cały artykuł można znaleźć na mój blog

Hibernate daje możliwość zdefiniowania metody pobierania asocjacji nie tylko w czasie projektowania, ale także w czasie wykonywania zapytania. Tak więc używamy tego aprochu w połączeniu z prostym relfection stuff I możemy również zautomatyzować proces zmiany właściwości kwerendy algorytmu pobierania tylko dla właściwości kolekcji.

Najpierw tworzymy metodę, która rozwiązuje wszystkie właściwości kolekcji z klasy encji:

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

Po zrobieniu tego możesz użyć tej małej metody pomocniczej doradzić obiekt criteria, aby zmienić FetchMode, aby wybrać w tym zapytaniu.

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

Robienie tego różni się od definiowania kodu pobierania elementów w czasie projektowania. Możesz więc użyć zwykłego skojarzenia join na algorytmach stronicowania w interfejsie użytkownika, ponieważ w większości przypadków nie jest to część krytyczna i ważniejsze jest, aby wyniki były tak szybkie, jak to możliwe.

 1
Author: Andreas Hartmann-schneevoigt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-06 14:44:45

Poniżej jest sposób, w jaki możemy wykonać wiele projekcji, aby wykonać różne

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

}

Ekstraprojekcje.java

package org.hibernate.criterion; 

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

Przykładowe Użycie:

String propertyNames = "titleName;titleDescr;titleVersion"

criteria countCriteria = ....

countCriteria.setProjection(ExtraProjections.countMultipleDistinct(propertyNames);

Https://forum.hibernate.org/viewtopic.php?t=964506

 0
Author: Yashpal Singla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-08 07:21:11

NullPointerException w niektórych przypadkach! Bez criteria.setProjection(Projections.distinct(Projections.property("id"))) wszystkie zapytania idą dobrze! To rozwiązanie jest złe!

Innym sposobem jest użycie SQLQuery. W moim przypadku następujący kod działa dobrze:

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

Rozróżnienie odbywa się w bazie danych! W przeciwieństwie do:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

Gdzie rozróżnienie odbywa się w pamięci, po załadowaniu jednostek!

 0
Author: Krzysztof Barczyński,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-20 16:23:32