2011-01-02 18 views
29

Ho utilizzato le restrizioni di ibernazione in JPA 1.0 (driver Hibernate). È definito Restrictions.ilike("column","keyword", MatchMode.ANYWHERE) che verifica se la parola chiave corrisponde alla colonna in qualsiasi punto e non fa distinzione tra maiuscole e minuscole.JPA2: senza distinzione tra maiuscole e minuscole come l'abbinamento ovunque

Ora, sto usando JPA 2.0 con EclipseLink come driver, quindi devo usare "Restrizioni" build-in JPA 2.0. Ho trovato CriteriaBuilder e il metodo like, ho anche scoperto come farlo corrispondere ovunque (anche se è impressionante e manuale), ma ancora non ho capito come farlo senza distinzione tra maiuscole e minuscole.

C'è il mio attuale soluzione pessima:

CriteriaBuilder builder = em.getCriteriaBuilder(); 
CriteriaQuery<User> query = builder.createQuery(User.class); 
EntityType<User> type = em.getMetamodel().entity(User.class); 
Root<User> root = query.from(User.class); 

// Where 
// important passage of code for question 
query.where(builder.or(builder.like(root.get(type.getDeclaredSingularAttribute("username", String.class)), "%" + keyword + "%"), 
     builder.like(root.get(type.getDeclaredSingularAttribute("firstname", String.class)), "%" + keyword + "%"), 
     builder.like(root.get(type.getDeclaredSingularAttribute("lastname", String.class)), "%" + keyword + "%") 
     )); 

// Order By 
query.orderBy(builder.asc(root.get("lastname")), 
      builder.asc(root.get("firstname"))); 

// Execute 
return em.createQuery(query). 
      setMaxResults(PAGE_SIZE + 1). 
      setFirstResult((page - 1) * PAGE_SIZE). 
      getResultList(); 

Domande:

C'è qualche funzione come nel conducente Ibernazione?

Sto utilizzando correttamente i criteri JPA 2.0? Questa è una soluzione scomoda e scomoda in confronto alle restrizioni di ibernazione.

Oppure qualcuno può aiutarmi a cambiare la mia soluzione senza distinzione tra maiuscole e minuscole, per favore?

Grazie mille.

risposta

57

All'inizio può sembrare un po 'imbarazzante, ma è sicuro per il tipo. La creazione di query da stringhe non lo è, quindi si notano errori in fase di esecuzione anziché in fase di compilazione. È possibile rendere le query più leggibili utilizzando le indentazioni o eseguendo ogni passaggio separatamente, invece di scrivere un'intera clausola WHERE in una singola riga.

Per rendere la query case-insensitive, convertire sia la parola chiave e il campo rispetto a abbassare caso:

query.where(
    builder.or(
     builder.like(
      builder.lower(
       root.get(
        type.getDeclaredSingularAttribute("username", String.class) 
       ) 
      ), "%" + keyword.toLowerCase() + "%" 
     ), 
     builder.like(
      builder.lower(
       root.get(
        type.getDeclaredSingularAttribute("firstname", String.class) 
       ) 
      ), "%" + keyword.toLowerCase() + "%" 
     ), 
     builder.like(
      builder.lower(
       root.get(
        type.getDeclaredSingularAttribute("lastname", String.class) 
       ) 
      ), "%" + keyword.toLowerCase() + "%" 
     ) 
    ) 
); 
+6

Sono anche migrando da Hibernate a JPA e ho trovato l'API di JPA per essere un po '... non chiara a volte. E sembra che ci siano molti modi diversi per realizzare la stessa cosa - alcuni più prolissi di altri. Credo che questo sarebbe un modo "più amichevole" di formare affermazioni simili: builder.like (builder.lower (radice: get ("username")), "%" + keyword.toLowerCase() + "%") – spaaarky21

+0

Penso che sia meglio usare un [JPA Static Metamodel Generator] (https://docs.jboss.org/hibernate/orm/5.0/topical/html/metamodelgen/MetamodelGenerator.html). Assicurerà che le proprietà esistano in fase di compilazione e conosce i tipi ;-) – TheConstructor

+0

Attenzione, confrontare la stringa abbassata dall'SGBD con una stringa abbassata da java può causare differenze per il carattere accentato. Esempio: Java: "EÉÊÈ" .toLower() = "eÉÊÈ" mentre postgresql inferiore ('EÉÊÈ') = 'eéêè'. Sto cercando un modo per dedicare de 'lower' tutti al SGBD – Ghurdyl

3

Più facile e più efficace per applicare caso insensitity all'interno del database di APP.

  1. Sotto la SQL 2003, 2006, standard di 2008, può fare questo con l'aggiunta di COLLATE SQL_Latin1_General_CP1_CI_AS O COLLATE latin1_general_cs al seguente:

    • Colonna Definizione

      CREATE TABLE <table name> (
          <column name> <type name> [DEFAULT...] 
                [NOT NULL|UNIQUE|PRIMARY KEY|REFERENCES...] 
                [COLLATE <collation name>], 
          ... 
      ) 
      
    • definizione di dominio

      CREATE DOMAIN <domain name> [ AS ] <data type> 
          [ DEFAULT ... ] [ CHECK ... ] [ COLLATE <collation name> ] 
      
    • Character Set Definizione

      CREATE CHARACTER SET <character set name> 
      [ AS ] GET <character set name> [ COLLATE <collation name> ] 
      

    Per la descrizione completa di cui sopra si riferiscono: http://savage.net.au/SQL/sql-2003-2.bnf.html#column%20definition http://dev.mysql.com/doc/refman/5.1/en/charset-table.html http://msdn.microsoft.com/en-us/library/ms184391.aspx

  2. In Oracle, è possibile impostare i parametri NLS Session/configurazione

    SQL> ALTER SESSION SET NLS_COMP=LINGUISTIC; 
    SQL> ALTER SESSION SET NLS_SORT=BINARY_CI; 
    SQL> SELECT ename FROM emp1 WHERE ename LIKE 'McC%e'; 
    
    ENAME 
    ---------------------- 
    McCoye 
    Mccathye 
    
    .210

    O, in init.ora (o il nome specifico sistema operativo per i file dei parametri di inizializzazione):

    NLS_COMP=LINGUISTIC 
    NLS_SORT=BINARY_CI 
    

    tipi binari possono essere maiuscole e minuscole o di un accento-insensitive. Quando si specifica BINARY_CI come valore per NLS_SORT, viene designato un ordinamento che è sensibile all'accento e maiuscolo/minuscolo. BINARY_AI designa un ordinamento binario insensibile al maiuscolo e al maiuscolo. Potresti voler usare un ordinamento binario se l'ordinamento binario del set di caratteri è appropriato per il set di caratteri che stai utilizzando. utilizzare il parametro della sessione NLS_SORT per specificare un case-insensitive o di un accento-insensitive sorta:

    Append _CI to a sort name for a case-insensitive sort. 
    Append _AI to a sort name for an accent-insensitive and case-insensitive sort. 
    

    Ad esempio, è possibile impostare NLS_SORT ai seguenti tipi di valori:

    FRENCH_M_AI 
    XGERMAN_CI 
    

    Impostazione NLS_SORT a nulla diverso da BINARY [con opzionale _CI o _AI], un ordinamento utilizza una scansione completa della tabella, indipendentemente dal percorso scelto dall'ottimizzatore. BINARY è l'eccezione perché gli indici sono costruiti secondo un ordine binario di chiavi. Pertanto, l'ottimizzatore può utilizzare un indice per soddisfare la clausola ORDER BY quando NLS_SORT è impostato su BINARY. Se NLS_SORT è impostato su un qualsiasi ordinamento linguistico, l'ottimizzatore deve includere una scansione completa della tabella e un ordinamento completo nel piano di esecuzione.

    Oppure, se NLS_COMP è impostato LINGUISTICO, come sopra, quindi ordinare impostazioni possono essere applicate localmente colonne indicizzate, piuttosto che a livello globale di tutti i database:

    CREATE INDEX emp_ci_index ON emp (NLSSORT(emp_name, 'NLS_SORT=BINARY_CI')); 
    

    Riferimento: ORA 11g Linguistic Sorting and String Searching ORA 11g Setting Up a Globalization Support Environment

+0

Il tuo link punta ancora a questa stessa domanda. Penso che la ricorsione non sia ciò che volevi;) – joe776

2

questo lavoro per me:

CriteriaBuilder critBuilder = em.getCriteriaBuilder(); 

CriteriaQuery<CtfLibrary> critQ = critBuilder.createQuery(Users.class); 
Root<CtfLibrary> root = critQ.from(Users.class); 

Expression<String> path = root.get("lastName"); 
Expression<String> upper =critBuilder.upper(path); 
Predicate ctfPredicate = critBuilder.like(upper,"%stringToFind%") 
critQ.where(critBuilder.and(ctfPredicate)); 
em.createQuery(critQ.select(root)).getResultList(); 
+0

Dove nel tuo codice ti assicuri che 'stringToFind' è maiuscolo? –

1

Soluzione alternativa per OpenJPA 2.3.0 e PostgreSQL

public class OpenJPAPostgresqlDictionaryPatch extends PostgresDictionary { 

    @Override 
    public SQLBuffer toOperation(String op, SQLBuffer selects, SQLBuffer from, SQLBuffer where, SQLBuffer group, SQLBuffer having, SQLBuffer order, boolean distinct, long start, long end, String forUpdateClause, boolean subselect) { 
    String whereSQL = where.getSQL(); 
    int p = whereSQL.indexOf("LIKE"); 
    int offset = 0; 
    while (p != -1) { 
     where.replaceSqlString(p + offset, p + offset + 4, "ILIKE"); 
     p = whereSQL.indexOf("LIKE", p + 1); 
     offset++; 
    } 
    return super.toOperation(op, selects, from, where, group, having, order, distinct, start, end, forUpdateClause, subselect); 
    } 

} 

Si tratta di una soluzione fragile e brutto per fare caso il funzionamento e minuscole come con il database OpenJPA e PostgreSQL. Sostituisce l'operatore LIKE con l'operatore ILIKE nell'SQL generato.

È inutile che OpenJPA DBDictionary non consenta di modificare i nomi degli operatori.

0

Si prega di prendere in considerazione di utilizzare

CriteriaBuilder.like(Expression<String> x, Expression<String> pattern, char escapeChar); 

per la corrispondenza da nessuna parte.

1

Come ho commentato la risposta (attualmente) accettato, c'è un trabocchetto utilizzare la funzione on uno DBMS mano' lower() e dell'altra java mano String.toLowerCase() sia come metodo non sono garantite per fornire la stessa uscita per la stessa stringa di input .

ho finalmente trovato una soluzione molto più sicuro che è quello di lasciare che i DBMS fare tutto l'abbassamento tramite espressioni letterali:

builder.lower(builder.literal("%" + keyword + "%") 

Quindi la soluzione completa sarà simile:

query.where(
    builder.or(
     builder.like(
      builder.lower(
       root.get(
        type.getDeclaredSingularAttribute("username", String.class) 
       ) 
      ), builder.lower(builder.literal("%" + keyword + "%") 
     ), 
     builder.like(
      builder.lower(
       root.get(
        type.getDeclaredSingularAttribute("firstname", String.class) 
       ) 
      ), builder.lower(builder.literal("%" + keyword + "%") 
     ), 
     builder.like(
      builder.lower(
       root.get(
        type.getDeclaredSingularAttribute("lastname", String.class) 
       ) 
      ), builder.lower(builder.literal("%" + keyword + "%") 
     ) 
    ) 
); 
Problemi correlati