質問

私はJPA 1.0(Hibernate Driver)でHibernateの制限を使用しています。定義されています Restrictions.ilike("column","keyword", MatchMode.ANYWHERE) キーワードが列をどこにでも一致させるかどうかをテストし、それがケース非感受性であるかどうかをテストします。

現在、EclipselinkをドライバーとしてJPA 2.0を使用しているため、「制限」ビルドインJPA 2.0を使用する必要があります。私は見つけました CriteriaBuilder およびメソッド like, 、私はそれをどこでも一致させる方法を見つけました(それは驚くほどマニュアルですが)が、それでも私はそれを行う方法をケースに感動的に理解していません。

私の現在の恐ろしい解決策があります:

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基準を正しく使用していますか?これは、冬眠制限に比べて厄介で不快な解決策です。

それとも、誰かが私のソリューションをケースに依存しないように変更する方法を手伝ってくれますか?

どうもありがとう。

役に立ちましたか?

解決

最初は少し厄介に思えるかもしれませんが、タイプセーフです。文字列からのクエリの構築はそうではないため、コンパイル時ではなく実行時にエラーが発生します。 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() + "%"
        )
    )
);

他のヒント

(現在)受け入れられている答えでコメントしたように、片手でDBMSを使用した落とし穴があります。 lower() 機能と一方、Javaの機能 String.toLowerCase() 両方の方法は、同じ入力文字列に対して同じ出力を提供するために保証されていません。

私は最終的に、DBMSが文字通りの表現を使用してすべての低下を行わせることである、はるかに安全な(まだ弾丸ではない)ソリューションを見つけました。

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

したがって、完全なソリューションは次のようになります。

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

編集:
@cavpolloが私に模範を示すように要求したので、私は自分の解決策についてよく考えなければならず、それが受け入れられている答えよりもそれほど安全ではないことに気付かなければなりませんでした:

DB value* | keyword | accepted answer | my answer
------------------------------------------------
elie     | ELIE    | match           | match
Élie     | Élie    | no match        | match
Élie     | élie    | no match        | no match
élie     | Élie    | match           | no match

それでも、私は自分の解決策を好む。すべての文字配列にまったく同じ関数を適用して、出力を比較することがより「安定した」ようになります。

防弾ソリューションにはロケールが含まれるため、SQLが lower() アクセントのある文字を正しく低下させることができます。 (しかし、これは私の謙虚な知識を超えています)

*ポストグレスクルのdb値9.5.1 with 'c' locale

私のためのこの仕事:

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

JPAよりもデータベース内でケースの不感を強制する方が簡単かつ効率的です。

  1. SQL 2003、2006、2008規格の下で、追加することでこれを行うことができます COLLATE SQL_Latin1_General_CP1_CI_AS また 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. Oracleでは、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
    

    バイナリソートは、ケース非感受性またはアクセント非感受性です。 binary_ciをnls_sortの値として指定すると、アクセントに敏感でケース感受性のあるソートを指定します。 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を次のタイプの値に設定できます。

    FRENCH_M_AI
    XGERMAN_CI
    

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

    または、上記のようにnls_compが言語に設定されている場合、データベース全体でグローバルにではなく、並べ替え設定をインデックス付き列にローカルに適用できます。

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

    参照: Ora 11g言語ソーティングと文字列検索 ORA 11Gグローバリゼーションサポート環境を設定します

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データベースを使用して操作のように症例の鈍感なケースの鈍感なことを行うための壊れやすく醜い回避策です。生成されたSQLのIlikeオペレーターに同様の演算子を置き換えます。

OpenJPA DBDictionaryがオペレーター名を変更することを許可していないことは残念です。

使用することを検討してください

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

どこでも一致するため。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top