2011-01-02 14 views
29

Ich habe Hibernate-Einschränkungen in JPA 1.0 (Hibernate-Treiber) verwendet. Es ist Restrictions.ilike("column","keyword", MatchMode.ANYWHERE) definiert, die testet, ob das Schlüsselwort die Spalte irgendwo und die Groß-/Kleinschreibung nicht beachtet.JPA2: Groß-/Kleinschreibung nicht beachten, wie überall passend

Jetzt benutze ich JPA 2.0 mit EclipseLink als Treiber, also muss ich "Einschränkungen" einbauen JPA 2.0 verwenden. Ich fand CriteriaBuilder und Methode like, ich habe auch herausgefunden, wie man es überall (obwohl es aweful und manuell ist), aber immer noch habe ich nicht herausgefunden, wie es die Groß-/Kleinschreibung nicht beachten.

Es ist meine aktuelle aweful Lösung:

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

Fragen:

Gibt es eine Funktion wie in Hibernate-Treiber?

Verwende ich die JPA 2.0-Kriterien richtig? Dies ist eine peinliche und unkomfortable Lösung im Vergleich zu Einschränkungen im Ruhezustand.

Oder kann mir bitte jemand helfen, meine Lösung so zu ändern, dass die Groß-/Kleinschreibung nicht beachtet wird?

Vielen Dank.

Antwort

57

Es mag zunächst ein wenig peinlich erscheinen, aber es ist typsicher. Das Erstellen von Abfragen aus Zeichenfolgen ist nicht möglich, sodass Sie Fehler zur Laufzeit statt zur Kompilierungszeit bemerken. Sie können die Abfragen lesbarer machen, indem Sie Einrückungen verwenden oder jeden Schritt einzeln ausführen, anstatt eine gesamte WHERE-Klausel in einer einzelnen Zeile zu schreiben.

Um Ihre Anfrage Groß- und Kleinschreibung zu machen, konvertieren sowohl Ihr Keyword und das verglichene Feld in Kleinbuchstaben:

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

Ich bin auch Migration von Hibernate zu JPA und ich habe festgestellt, dass JPA's API ein wenig ... unklar zu Zeiten ist. Und es scheint eine Menge verschiedener Möglichkeiten zu geben, dasselbe zu erreichen - einige mehr als andere. Ich glaube, das wäre eine "freundlichere" Art, solche Aussagen zu bilden: builder.like (builder.lower (root. get ("benutzername")), "%" + keyword.toLowerCase() + "%") – spaaarky21

+0

Ich denke, es ist am besten, einen [JPA Static Metamodel Generator] zu verwenden (https://docs.jboss.org/hibernate/orm/5.0/topical/html/metamodelgen/MetamodelGenerator.html). Es stellt sicher, dass die Eigenschaften zum Zeitpunkt der Kompilierung existieren und kennt die Typen ;-) – TheConstructor

+0

Vorsicht, der Vergleich der durch SGBD gesenkten Zeichenkette mit der durch Java gesenkten Zeichenkette kann zu Unterschieden bei akzentuierten Zeichen führen. Beispiel: Java: "EÉÊÈ" .toLower() = "eÉÊÈ" während postgresql niedriger ('EÉÊÈ') = 'eéêè'. Ich bin auf der Suche nach einer Möglichkeit, de 'Lower' alle auf die SGBD widmen – Ghurdyl

3

einfacher und effizienter Fall insensitity innerhalb der Datenbank als PPV zu erzwingen.

  1. Unter dem SQL 2003, 2006, 2008 Standards, wird dies durch das Hinzufügen COLLATE SQL_Latin1_General_CP1_CI_AS OR COLLATE latin1_general_cs auf folgende Möglichkeiten:

    • Spaltendefinition

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

      CREATE DOMAIN <domain name> [ AS ] <data type> 
          [ DEFAULT ... ] [ CHECK ... ] [ COLLATE <collation name> ] 
      
    • Zeichensatz Definition

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

    Ausführliche Beschreibung siehe oben: 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 können NLS Session/Konfigurationsparameter eingestellt

    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 
    

    Oder in init.ora (oder OS spezifischen Namen für die Initialisierung Parameterdatei):

    NLS_COMP=LINGUISTIC 
    NLS_SORT=BINARY_CI 
    

    Binary Art Groß- und Kleinschreibung oder akzent unempfindlich sein kann. Wenn Sie BINARY_CI als einen Wert für NLS_SORT angeben, bezeichnet es eine Sortierung, die Akzent-sensitive und Groß-und Kleinschreibung nicht berücksichtigt. BINARY_AI bezeichnet eine binäre Sortierung ohne Akzent und ohne Berücksichtigung der Groß- und Kleinschreibung. Sie können eine binäre Sortierung verwenden, wenn die binäre Sortierreihenfolge des Zeichensatzes für den von Ihnen verwendeten Zeichensatz geeignet ist. Verwenden Sie die NLS_SORT Session-Parameter ein Groß- und Kleinschreibung oder Akzent unempfindliche Art angeben:

    FRENCH_M_AI 
    XGERMAN_CI 
    

    Einstellung NLS_SORT auf alles:

    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. 
    

    Zum Beispiel können Sie NLS_SORT auf die folgenden Arten von Werten eingestellt Anders als BINARY [mit optionalem _CI oder _AI] bewirkt eine Sortierung die Verwendung eines vollständigen Tabellenscan, unabhängig vom vom Optimierungsprogramm ausgewählten Pfad. BINARY ist die Ausnahme, da Indizes nach einer binären Schlüsselreihenfolge erstellt werden. Daher kann der Optimierer einen Index verwenden, um die ORDER BY-Klausel zu erfüllen, wenn NLS_SORT auf BINARY gesetzt ist. Wenn NLS_SORT auf eine linguistische Sortierung gesetzt ist, muss das Optimierungsprogramm einen vollständigen Tabellenscan und eine vollständige Sortierung im Ausführungsplan enthalten.

    Oder, wenn NLS_COMP auf SPRACH gesetzt ist, wie oben beschrieben, dann Einstellungen lokal auf indizierte Spalten angewendet werden, sortieren, anstatt global über die Datenbank:

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

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

+0

Ihr Link verweist erneut auf diese Frage. Ich denke, Rekursion ist nicht das, was Sie wollten;) – joe776

2

Diese Arbeit für mich:

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

Wo in Ihrem Code stellen Sie sicher, dass 'stringToFind' Großbuchstaben ist? –

1

Problemumgehung für OpenJPA 2.3.0 und 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); 
    } 

} 

Dies ist eine fragile und hässlich Abhilfe für mit OpenJPA und Postgresql Datenbank Groß- und Kleinschreibung wie Operation zu tun. Es ersetzt den LIKE-Operator durch den ILIKE-Operator im generierten SQL.

Es ist schade, dass OpenJPA DBDictionary es nicht erlaubt, die Namen von Benutzern zu ändern.

0

Bitte beachten Sie

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

zur Anpassung überall zu verwenden.

1

Wie ich in der (derzeit) akzeptierte Antwort kommentiert, gibt es eine Gefahr mit einerseits DBMS‘lower() Funktion und auf der anderen Seite Javas String.toLowerCase(), da beide Verfahren sind warrantied nicht die gleiche Leistung für die gleiche Eingabekette zu schaffen .

Ich fand schließlich eine viel sicherere Lösung, die das DBMS alles tun, die Senkung mit einem literalen Ausdruck zu lassen ist:

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

aussehen würde, also die komplette Lösung wie:

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 + "%") 
     ) 
    ) 
);