Criteri API restituisce un gruppo di risultati troppo piccolo
-
24-09-2019 - |
Domanda
Come è possibile, devo seguenti criteri
Criteria criteria = getSession().createCriteria(c);
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
criteria.add(Restrictions.eq("active",true));
List list = criteria.list();
La dimensione della lista è ora 20. Se posso aggiungere un massimo di risultati per i criteri,
Criteria criteria = getSession().createCriteria(c);
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
criteria.setMaxResults(90);
criteria.add(Restrictions.eq("active",true));
List list = criteria.list();
.. ora le dimensioni di lista è 18!
Non capisco come la dimensione di risultati può essere più piccolo dopo aver definito i risultati massimi, come la quantità di righe è più piccolo del massimo definito. Questo sembra sicuro come un insetto, o c'è ancora alcuni aspetti strani di Hibernate che io non sono a conoscenza?
Se siete alla ricerca di una risposta a questa domanda, assicuratevi di leggere la risposta accettata e le sue osservazioni.
Soluzione
Quello che sta accadendo qui può essere visto molto chiaramente accendendo debug SQL in Hibernate e confrontando le query generate.
Utilizzando un abbastanza semplice Sale
→ Item
uno-a-molti mappatura (che è si spera auto-esplicativo), una query Criteria
-based in questo modo:
Criteria c = sessionFactory.getCurrentSession().createCriteria(Sale.class);
c.createAlias("items", "i");
c.add(Restrictions.eq("i.name", "doll"));
c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
c.setMaxResults(2);
produce SQL come questo:
select top ? this_.saleId as saleId1_1_, ...
from Sale this_
inner join Sale_Item items3_ on this_.saleId=items3_.Sale_saleId
inner join Item items1_ on items3_.items_id=items1_.id
where items1_.name=?
mentre un Query
in questo modo:
Query q = sessionFactory.getCurrentSession().createQuery("select distinct s from Sale s join s.items as i where i.name=:name");
q.setParameter("name", "doll");
q.setMaxResults(2);
produce qualcosa come:
select top ? distinct hibernated0_.saleId as saleId1_
from Sale hibernated0_
inner join Sale_Item items1_ on hibernated0_.saleId=items1_.Sale_saleId
inner join Item hibernated2_ on items1_.items_id=hibernated2_.id
where hibernated2_.name=?
Si noti la differenza nella prima riga (DISTINCT
). Un ResultTransformer
come DISTINCT_ROOT_ENTITY
è una classe Java, che elabora i risultati delle file SQL dopo viene eseguito il codice SQL. Pertanto, quando si specifica un maxResults
, che si intende applicare come limite fila sulla SQL; SQL include un join sugli elementi del Collection
, quindi si sta limitando il risultato SQL a 90 sub-elementi . Una volta che il trasformatore DISTINCT_ROOT_ENTITY
viene applicata, che può risultare in meno di 20 elementi radice, puramente dipende da quale radice elementi capita di uscire prima nei 90 risultati uniti.
DISTINCT
in HQL comporta molto diverso, dal fatto che in realtà utilizza il DISTINCT
parola chiave SQL, che viene applicato prima il limite di riga. Pertanto, questo si comporta come ci si aspetta, e spiega la differenza tra il 2.
In teoria si dovrebbe guardare setProjection
di applicare una proiezione a livello di SQL - qualcosa come c.setProjection(Projections.distinct(Projections.rootEntity()))
- ma purtroppo Projections.rootEntity()
non esiste, ho appena fatto in su. Forse si deve!
Altri suggerimenti
I setMaxResults non funziona con unirsi query SQL esterne. Forse questo è il problema: Hibernate non restituisce i risultati distinti per una query con join esterno abilitato per un raccolta (anche se io uso la parola distinti)? .
Spero che questo può essere di aiuto
public List<Employee> getData(int to, int from) {
Criteria hCriteria = null;
List<Employee> viewDataList = null;
List<Employee> exactDataList = null;
try {
hSession = HibernateSessionFactory.getSession();
hTransaction = hSession.beginTransaction();
hCriteria = hSession.createCriteria(Employee.class);
/*
hCriteria.setFirstResult(to);
hCriteria.setFirstResult(from);
*/
hCriteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
viewDataList = hCriteria.list();
// for limit
exactDataList=viewDataList.subList(from,to);
hTransaction.commit();
} catch (Exception e) {
hTransaction.rollback();
} finally {
try {
hSession.flush();
HibernateSessionFactory.closeSession();
} catch (Exception hExp) {
}
}
return exactDataList;
}
Un'altra soluzione è il seguente:
- eseguire il
criteria.list()
senza impostare alcun alias => i set / lista del soggetto principale riferimento sarà riempito con i proxy => Qui è possibile impostare correttamente i massimi risultati e quali - eseguire i criteri alias sul proprio nella stessa sessione di sospensione => le deleghe di cui sopra verranno inizializzate
Qualcosa di simile a questo:
Criteria criteria = this.getSession().createCriteria(User.class);
criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
criteria.setMaxResults(10);
// First get the results without joining with the other tables
List<User> results = criteria.list();
// at this point the set roles is filled with proxies
// we'll now create and execute the join so these proxies are filled since we're still in the same session
getSession().createCriteria(User.class, "u")
.createAlias("u.roles", "r", CriteriaSpecification.LEFT_JOIN)
.list();
return results;
Spero che questo può aiutare,
Stijn
Questo è un problema noto in sospensione. Guardate @Cowan per uno SQL generato e una spiegazione del problema. C'è una richiesta di bug aperti per questo nel loro JIRA. La speranza di lasciare che qualcuno arriva e lo fissa:)