2015-07-07 13 views
19

Ho bisogno di inviare una query per recuperare i valori che ha un gruppo specifico di caratteri come segue:Come ordinare risultato di ibernazione in base a un ordine specifico

Diciamo Sono interessato a 'XX' quindi dovrebbe cercare per ogni campo il suo valore inizia con 'XX' o ha 'XX' (spazio XX). Ad esempio XXCDEF, PD XXRF e CMKJIEK XX sono risultati validi.

Ho seguente query che restituisce i risultati corretti, ma ho bisogno di ordinarli in un modo che prima tornare quelli con XX all'inizio poi altri risultati. Come segue:

XXABCD 
XXPLER 
XXRFKF 
AB XXAB 
CD XXCD 
ZZ XXOI 
POLO XX 

Codice

Criteria criteria = session.createCriteria(Name.class, "name") 
       .add(Restrictions.disjunction() 
        .add(Restrictions.ilike("name.fname", fname + "%")) 
        .add(Restrictions.ilike("name.fname", "%" + " " + fname + "%")) 
        ) 
       .setProjection(Projections.property("name.fname").as("fname")); 
     List<String> names = (List<String>) criteria.list(); 
+2

Non so quale ordine passi dalla riga 4 alla riga 7? – Jegg

+0

@Jegg Dovrei mostrare il gruppo di stringhe che hanno XX all'inizio. Il resto (riga 4 a riga7) può essere quello che ha XX (spazio XX) nel mezzo seguito da quelli che hanno XX (spazio XX) alla fine, un'altra opzione è applicare l'ordine alfabetico una riga da 4 a 7. – Jack

+0

Perché no ordinali in java una volta recuperati i risultati? –

risposta

15

Con JPQL (HQL):

select fname from Name 
where upper(fname) like :fnameStart or upper(fname) like :fnameMiddle 
order by (case when upper(fname) like :fnameStart then 1 else 2 end), fname 

query.setParameter("fnameStart", "XX%"); 
query.setParameter("fnameMiddle", "% XX%"); 

Con Criteri

Con Criteria è molto più complicato. In primo luogo, è necessario ricorrere a SQL nativo nella clausola order. In secondo luogo, devi associare la variabile.

public class FirstNameOrder extends Order { 
    public FirstNameOrder() { 
     super("", true); 
    } 

    @Override 
    public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException { 
     return "case when upper(FIRST_NAME) like ? then 1 else 2 end"; 
    } 
} 

L'espressione sintassi caso e il nome upper funzione dovrebbe essere cambiato in conformità con il database (e il nome della colonna, se è diverso, naturalmente).

È facile aggiungerlo allo Criteria, ma non esiste alcuna API per associare il parametro.

ho cercato di ingannare Hibernate passando una variabile non utilizzata per la restrizione di sql personalizzato in modo che esso sia effettivamente utilizzato per la variabile nella clausola order by:

Criteria criteria = session.createCriteria(Name.class, "name") 
    .add(Restrictions.disjunction() 
     .add(Restrictions.ilike("name.fname", fname + "%")) 
     .add(Restrictions.ilike("name.fname", "%" + " " + fname + "%"))) 
    .setProjection(Projections.property("name.fname").as("fname")) 
    .add(Restrictions.sqlRestriction("1 = 1", fname + "%", StringType.INSTANCE)) 
    .addOrder(new FirstNameOrder()) 
    .addOrder(Order.asc("fname")); 

e funziona benissimo.

Ovviamente, questa soluzione non è consigliata e suggerisco di utilizzare JPQL per questa query.

+0

Come OP che utilizza Hibernate HQL è più appropriato. –

+0

Vero, ma la query dovrebbe essere la stessa. –

+0

@RomanC cosa significa PO? –

0

stupido, ma può funzionare per il vostro caso.

Dal momento che avete ottenuto il risultato corretto si può solo ricostruire i risultati come segue:

  1. raccogliere tutti i risultati a partire dal XX li metti in una lista L1 e fare il tipo normale come Collections.sort (L1);
  2. per tutti gli altri risultati, fanno lo stesso come Collections.sort (L2) elenco dei L2
  3. Infine, unirli

    Elenco newList = new ArrayList (L1);

    newList.addAll (L2);

Nota. Collections.sort segue l'ordine naturale dei suoi elementi.

+1

Un'altra opzione è recuperare gli elenchi separati da sospensione e unirli insieme, ma mi chiedo se è possibile recuperarli tutti da una singola query. – Jack

2

Esegui due selezioni, una filtrata per tutte le stringhe che iniziano con 'XX', la seconda filtrata per le altre.

1

È possibile utilizzare predicati a criteri ... qualcosa di simile:

public List<Name> findByParameter(String key, String value, String orderKey) 

      CriteriaBuilder builder = this.entityManager.getCriteriaBuilder(); 
      CriteriaQuery<Name> criteria = builder.createQuery(this.getClazz()); 
      Root<Name> root = criteria.from(Name.getClass()); 
      criteria.select(root); 
      List<Predicate> predicates = new ArrayList<Predicate>(); 
      predicates.add(builder.equal(root.get(key), value)); 
      criteria.where(predicates.toArray(new Predicate[predicates.size()])); 
      if (orderKey!=null && !orderKey.isEmpty()) { 
       criteria.orderBy(builder.asc(root.get(orderKey))); 
      } 
      result = this.entityManager.createQuery(criteria).getResultList(); 

     return result; 
} 
+1

qual è la tua idea della risposta di Dragan? – Jack

0

Se non si desidera ordinare il risultato in memoria, è possibile modificare il criterio, ti faccio vedere la SQL

select * from table where fname like 'XX%' order by fname 
union all 
select * from table where fname like '% XX%' order by fname 
union all 
select * from table where fname like '% XX' order by fname 

il risultato sarà l'ordine e alfabetico e quindi applicare il filtro.