2011-01-02 2 views
29

JPA 1.0(Hibernateドライバ)でHibernate Restrictionsを使用しています。 Restrictions.ilike("column","keyword", MatchMode.ANYWHERE)が定義されています。このキーワードは、キーワードがどこにでも一致するかどうかをテストし、大文字と小文字を区別しません。JPA2:どこでもマッチするような大文字小文字の区別なし

EclipseLinkをドライバとしてJPA 2.0を使用していますので、JPA 2.0の「制限」を使用する必要があります。私はCriteriaBuilderと方法likeを見つけました。私はそれをどこでも一致させる方法を見つけましたが(それは恐ろしいマニュアルですが)、それでも大文字と小文字を区別しない方法を考え出していません。

私の現在のawefulソリューションがあります:

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

質問:

は、Hibernateのドライバのように任意の機能がありますか?

JPA 2.0基準を正しく使用していますか?これは、Hibernateの制限と比較して、扱いにくく、不快な解決策です。

また、大文字小文字を区別しないようにソリューションを変更する方法を教えてもらえますか?

ありがとうございます。

答えて

57

最初は少し厄介なように見えますが、タイプセーフです。文字列からクエリを作成するのではなく、コンパイル時ではなく実行時にエラーが発生します。 WHERE句全体を1行に書くのではなく、インデントを使用するか、各ステップを個別に実行することで、クエリをより読みやすくすることができます。

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

:だから完全なソリューションは次のようになり

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

私は最終的にリテラル式を使用してDBMSはすべて下げないようにすることですはるかに安全解決策を見つけましたまた、HibernateからJPAに移行すると、JPAのAPIが少しはっきりしないことが判明しました。同じことを達成するための方法はたくさんあるようです。他の方法よりも冗長な方法がいくつかあります。 builder.like(builder.lower(root。 get( "username"))、 "%" + keyword.toLowerCase()+ "%") – spaaarky21

+0

このようなステートメントを作成するには、私は[JPA静的メタモデルジェネレータ](https://docs.jboss.org/hibernate/orm/5.0/topical/html/metamodelgen/MetamodelGenerator.html)を使用するのが最善だと思います。それはコンパイル時にプロパティが存在することを保証し、型を知っている;-) – TheConstructor

+0

SGBDによって下げられた文字列とjavaによって下げられた文字列を比較することは、アクセント付き文字の違いを引き起こすかもしれません。 例:Java: "EÉÊÈ" .toLower()= "eÉÊÈ"、postgresql lower( 'EÉÊÈ')= 'eéêè'です。私はSGBDに全て「下位」を捧げる方法を探しています – Ghurdyl

3

より簡単かつ効率的にJPAよりも、データベース内のケースinsensitityを強制する:

は、キーワードと比較フィールドの両方を小文字に変換し、クエリでは大文字と小文字を区別しないようにするには。

SQL 2003、2006、2008年基準では
  1. 、次のようにCOLLATE SQL_Latin1_General_CP1_CI_AS OR COLLATE latin1_general_csを追加することによってこれを行うことができますことは:

    • 列定義

      CREATE TABLE <table name> (
          <column name> <type name> [DEFAULT...] 
                [NOT NULL|UNIQUE|PRIMARY KEY|REFERENCES...] 
                [COLLATE <collation name>], 
          ... 
      ) 
      
    • ドメイン定義

      CREATE DOMAIN <domain name> [ AS ] <data type> 
          [ DEFAULT ... ] [ CHECK ... ] [ COLLATE <collation name> ] 
      
    • 上記の完全な説明については
    • 文字セット定義

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

    参照してください。 オラクルで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. 、NLSセッション/設定パラメータに

    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 
    
    を設定することができます

    それとも、中init.ora(または初期化パラメータ・ファイルのOS固有の名前):

    NLS_COMP=LINGUISTIC 
    NLS_SORT=BINARY_CI 
    

    バイナリソートは大文字と小文字を区別しないか、アクセントを区別しないことができます。 NLS_SORTの値としてBINARY_CIを指定すると、アクセントセンシティブで大文字小文字を区別しないソートを指定します。 BINARY_AIは、アクセント記号と大文字と小文字を区別しないバイナリソートを指定します。文字セットのバイナリソート順が使用している文字セットに適している場合は、バイナリソートを使用することができます。 は大文字と小文字を区別しないか、アクセントを区別しないソートを指定するには、NLS_SORTセッション・パラメータを使用します。

    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. 
    

    たとえば、あなたは以下の値のタイプにNLS_SORTを設定することができます。

    何にもNLS_SORTを設定する
    FRENCH_M_AI 
    XGERMAN_CI 
    

    BINARY [オプションの_CIまたは_AIを使用する]以外では、オプティマイザによって選択されたパスに関係なく、ソートで全表スキャンが使用されます。インデックスはキーのバイナリ順序に従って作成されるため、BINARYは例外です。したがって、オプティマイザは、NLS_SORTがBINARYに設定されている場合にORDER BY句を満たす索引を使用できます。 NLS_SORTが言語ソートに設定されている場合、オプティマイザは、実行計画に完全な表スキャンと完全ソートを含める必要があります。

    あるいは、NLS_COMPをLINGUISTICに設定されている場合、上記のように、その後、ソートの設定がデータベースを横切ってグローバルに索引付けされた列に局所的に適用されるのではなく、することができる。

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

    リファレンス:ORA 11g Linguistic Sorting and String Searching ORA 11g Setting Up a Globalization Support Environment

私にとって
+0

あなたのリンクはこの非常に質問を再度指しています。私は再帰があなたが望んでいたものではないと思う;) – joe776

2

この作品:

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

あなたのコードのどこで 'stringToFind'が大文字であることを確認しますか? –

1

OpenJPA 2.3の厄介な回避策。0と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); 
    } 

} 

これは、OpenJPAのとPostgreSQLデータベースで大文字小文字を区別しないLIKE演算を行うための壊れやすいと醜い回避策です。生成されたSQLのLIKE演算子をILIKE演算子に置き換えます。

OpenJPA DBDictionaryが演算子名を変更できないことはあまりにも悪いことです。

0

どこにでもマッチするため

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

を使用することをご検討ください。

1

私が(現在)受け入れられている回答にコメントしたように、DBMSのlower()機能を使用している落とし穴があります。一方、javaのString.toLowerCase()は両方とも同じ入力文字列に対して同じ出力を保証するものではない。アイム

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 + "%") 
     ) 
    ) 
); 
関連する問題