2011-01-02 8 views
29

Używam ograniczeń Hibernate w JPA 1.0 (sterownik Hibernate). Zdefiniowano parametr Restrictions.ilike("column","keyword", MatchMode.ANYWHERE), który sprawdza, czy słowo kluczowe pasuje do kolumny w dowolnym miejscu i nie rozróżnia wielkości liter.JPA2: Niewrażliwe na wielkość liter jak dopasowanie wszędzie

Teraz używam JPA 2.0 z EclipseLink jako sterownika, więc muszę użyć "Ograniczenia" wbudowanego JPA 2.0. Znalazłem CriteriaBuilder i metodę like, również dowiedziałem się, jak sprawić, by pasowało to wszędzie (choć jest straszne i ręczne), ale nadal nie wiem, jak to zrobić, nie rozróżniając wielkości liter.

Jest mój obecny straszne rozwiązanie:

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

Pytania:

Czy istnieje funkcja jak w sterowniku hibernacji?

Czy poprawnie stosuję kryteria JPA 2.0? Jest to niewygodne i niewygodne rozwiązanie w porównaniu z ograniczeniami Hibernate.

Czy ktoś może mi pomóc, jak zmienić moje rozwiązanie, aby nie było w nim rozróżniane wielkości liter?

Wielkie dzięki.

Odpowiedz

57

Z początku może wydawać się to trochę dziwne, ale jest bezpieczne dla typu. Budowanie zapytań z łańcuchów nie jest, więc zauważasz błędy w środowisku wykonawczym zamiast w czasie kompilacji. Możesz sprawić, że zapytania będą bardziej czytelne, używając wcięć lub biorąc każdy krok osobno, zamiast pisać całą klauzulę WHERE w jednym wierszu.

Aby zapytanie wielkości liter, konwersja zarówno słowo kluczowe, a pola w stosunku do małych liter:

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

jestem również migracja z Hibernate do JPA i odkryłem, że API JPA jest trochę ... niejasne. I wydaje się, że istnieje wiele różnych sposobów osiągnięcia tego samego - niektóre bardziej szczegółowe niż inne. Wierzę, że byłby to "bardziej przyjazny" sposób tworzenia takich instrukcji: builder.like (builder.lower (root: get ("username")), "%" + keyword.toLowerCase() + "%") – spaaarky21

+0

Myślę, że najlepiej jest użyć [JPA Static Metamodel Generator] (https://docs.jboss.org/hibernate/orm/5.0/topical/html/metamodelgen/MetamodelGenerator.html). Zapewni to, że właściwości istnieją podczas kompilacji i zna typy ;-) – TheConstructor

+0

Uważaj, porównywanie ciągów obniżonych przez SGBD z ciągiem obniżonym przez java może spowodować różnice dla znaków akcentowanych. Przykład: Java: "EÉÊÈ" .toLower() = "eÉÊÈ", podczas gdy postgresql lower ('EÉÊÈ') = 'eéêè'. Szukam sposobu, aby poświęcić de "niższy" wszystko na SGBD – Ghurdyl

3

łatwiejsze i bardziej efektywne egzekwowanie case insensitity w bazie danych niż WZP.

  1. Pod SQL 2003, 2006, normy 2008, można to zrobić poprzez dodanie COLLATE SQL_Latin1_General_CP1_CI_AS LUB COLLATE latin1_general_cs do poniższego:

    • Kolumna Definicja

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

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

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

    Dla pełnego opisu powyżej patrz: 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. W Oracle, można ustawić parametry NLS Session/configuration

    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 
    

    Lub, init.ora (lub nazwa OS specyficzne dla inicjalizacji pliku parametrów):

    NLS_COMP=LINGUISTIC 
    NLS_SORT=BINARY_CI 
    

    binarne rodzaju może być wielkość liter lub niewrażliwe akcent. Po określeniu BINARY_CI jako wartości dla NLS_SORT, oznacza sortowanie, które jest wrażliwe na akcent i wielkość znaków. BINARY_AI wyznacza sortowanie binarne niewrażliwe na wielkość znaków i wielkość liter. Możesz użyć sortowania binarnego, jeśli kolejność sortowania binarnego zestawu znaków jest odpowiednia dla używanego zestawu znaków. Użyj parametru sesji NLS_SORT określenie wielkości liter lub akcent-niewrażliwy sortowania:

    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. 
    

    Na przykład, można ustawić NLS_SORT do następujących rodzajów wartości:

    FRENCH_M_AI 
    XGERMAN_CI 
    

    Ustawianie NLS_SORT do niczego inne niż BINARY [z opcjonalnym _CI lub _AI] powoduje, że sortowanie wykorzystuje pełne skanowanie tabeli, niezależnie od ścieżki wybranej przez optymalizator. BINARY jest wyjątkiem, ponieważ indeksy są budowane zgodnie z dwójkową kolejnością klawiszy. Dlatego optymalizator może użyć indeksu do spełnienia klauzuli ORDER BY, gdy NLS_SORT ma wartość BINARY. Jeśli NLS_SORT jest ustawiony na dowolny sortowanie lingwistyczne, optymalizator musi zawierać pełne skanowanie tabeli i pełne sortowanie w planie wykonania.

    Lub, jeśli NLS_COMP jest ustawiony językowej, jak wyżej, następnie posortować ustawienia mogą być stosowane lokalnie do indeksowanych kolumnach, zamiast globalnie w całej bazy:

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

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

+0

Twój link ponownie wskazuje na to pytanie. Myślę, że rekurencja nie jest tym, czego potrzebujesz;) – joe776

2

To praca dla mnie:

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

Gdzie w swoim kodzie upewniasz się, że 'stringToFind' jest wielkie? –

1

Pozbawione obejścia dla OpenJPA 2.3.0 oraz 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); 
    } 

} 

To jest kruchy i brzydki obejście robi wielkość liter ma znaczenie działanie jak z bazy OpenJPA i PostgreSQL. Zastępuje operatora LIKE do operatora ILIKE w generowanym SQL.

Szkoda, że ​​DBDictionary OpenJPA nie zezwala na zmianę nazw operatorów.

0

Proszę rozważyć użycie

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

dopasowywania wszędzie.

1

Jak skomentowałem w (obecnie) zaakceptowanej odpowiedzi, istnieje pułapka z jednej strony funkcji DBMS "lower(), az drugiej strony Javy String.toLowerCase(), ponieważ obie metody nie są gwarantowane, aby zapewnić to samo wyjście dla tego samego ciągu wejściowego .

końcu znalazłem dużo bezpieczniejsze rozwiązanie, które ma pozwolić DBMS zrobić wszystko obniżenie użyciu dosłowne wyrażenie:

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

więc kompletnym rozwiązaniem będzie wyglądać następująco:

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 + "%") 
     ) 
    ) 
); 
Powiązane problemy