Come ottenere risultati distinti in ibernazione con join e limitazione basata sulla riga (paging)?

StackOverflow https://stackoverflow.com/questions/300491

Altri suggerimenti

Sto usando questo con i miei codici.

Aggiungilo semplicemente ai tuoi criteri:

  

criteria.setResultTransformer (Criteria.DISTINCT_ROOT_ENTITY);

quel codice sarà come il distinto selezionare * dalla tabella del sql nativo. Spero che questo aiuti.

Un leggero miglioramento basato sul suggerimento di FishBoy.

È possibile eseguire questo tipo di query in un colpo, piuttosto che in due fasi separate. vale a dire che la singola query seguente mostrerà correttamente risultati distinti e restituirà anche entità anziché solo ID.

È sufficiente utilizzare un DetachedCriteria con una proiezione ID come sottoquery, quindi aggiungere i valori di paging sull'oggetto Criteria principale.

Sarà simile a questo:

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

Un piccolo miglioramento al suggerimento di @ FishBoy è l'uso della proiezione id, quindi non è necessario codificare il nome della proprietà dell'identificatore.

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

Questo mi ha aiutato: D

La soluzione:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

funziona molto bene.

se vuoi usare ORDER BY, aggiungi semplicemente:

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

Spiegherò ora una soluzione diversa, in cui è possibile utilizzare il normale metodo di query e impaginazione senza avere il problema di eventuali duplicati o elementi eliminati.

Questa soluzione ha il vantaggio di essere:

  • più veloce della soluzione ID PK menzionata in questo articolo
  • conserva l'ordinamento e non usa la "clausola in" su un set di dati possibilmente grande di PK & # 8217; s

L'articolo completo è disponibile su il mio blog

Hibernate offre la possibilità di definire il metodo di recupero dell'associazione non solo in fase di progettazione, ma anche in fase di esecuzione mediante l'esecuzione di una query. Quindi usiamo questo approccio insieme a una semplice roba di relection e possiamo anche automatizzare il processo di modifica dell'algoritmo di recupero delle proprietà della query solo per le proprietà della raccolta.

Per prima cosa creiamo un metodo che risolve tutte le proprietà della raccolta dalla Classe di entità:

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

Dopo averlo fatto, puoi utilizzare questo piccolo metodo di supporto e consiglia ai tuoi criteri di modificare l'oggetto FetchMode in SELECT su quella query.

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

Ciò è diverso dal definire la modalità Fetch delle entità in fase di progettazione. Quindi puoi usare il normale recupero delle associazioni di join sugli algoritmi di paging nella tua interfaccia utente, perché questa non è la parte critica, ed è più importante avere i risultati il ??più rapidamente possibile.

Di seguito è riportato il modo in cui possiamo eseguire la proiezione multipla per eseguire 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();
    }
}

Utilizzo di esempio:

String propertyNames = "titleName;titleDescr;titleVersion"

criteria countCriteria = ....

countCriteria.setProjection(ExtraProjections.countMultipleDistinct(propertyNames);

Citato da https://forum.hibernate.org/viewtopic.php?t= 964506

NullPointerException in alcuni casi! Senza criteri.setProjection (Projections.distinct (Projections.property (" id "))) tutte le domande vanno bene! Questa soluzione è pessima!

Un altro modo è usare SQLQuery. Nel mio caso il seguente codice funziona bene:

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

La distinzione viene fatta nella banca dati! Al contrario di:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

dove si fa distinzione in memoria, dopo aver caricato entità!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top