3

私が使っている、

  • JPA2.0
  • モハラ 2.1.9
  • JSF コンポーネント ライブラリ、Primefaces 3.5。
  • MySQL 5.6.11

state_table例として、3 つの列で名前が付けられた MySQL データベースのテーブルがあります。

  • state_id (BigInt)
  • state_name (Varchar)
  • country_id (BigInt)

state_id自動生成された主キーであり、テーブルcountry_idの主キーを参照する外部キーです。country


このテーブルは、名前が付けられた対応するエンティティ クラスによってマップされStateTable、このテーブルが保持するデータは PrimefacesDataTableに表示されます<p:dataTable>...</p:dataTable>

DataTable列ヘッダーには、クリック可能な並べ替え領域が含まれています。各列<div>には、並べ替えの並べ替え方向があり、この領域をクリックすると、並べ替え順序を表す文字列がレンダリングされ、ユーザーが入力するフィルタリング (検索) 用のテキスト ボックスが表示されます。各列の検索項目。ASCENDINGDESCENDING


最終的に、JSF マネージド Bean で得られるのは、ユーザーが希望するjava.util.List<org.primefaces.model.SortMeta>列の並べ替え順序を表す型のリストです。DataTable

java.util.Map<java.lang.String, java.lang.String>また、検索列名をキー、対応する列の検索項目を値として表すタイプの Map (検索項目は、 の各列の列ヘッダーのテキスト ボックスにユーザーが入力しますDataTable)。


つまり、List<SortMeta>並べ替えとMap<String, String>フィルタリング/検索に使用します。

並べ替えとフィルター処理の後に行のリストを取得するための DAO の 1 つのコードは次のとおりです。

@Override
@SuppressWarnings("unchecked")
public List<StateTable> getList(int first, int pageSize, List<SortMeta> multiSortMeta, Map<String, String>filters)
{
    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<StateTable> criteriaQuery = criteriaBuilder.createQuery(StateTable.class);
    Metamodel metamodel=entityManager.getMetamodel();
    EntityType<StateTable> entityType = metamodel.entity(StateTable.class);
    Root<StateTable>root=criteriaQuery.from(entityType);
    Join<StateTable, Country> join = null;

    //Sorting

    List<Order> orders=new ArrayList<Order>();

    if(multiSortMeta!=null&&!multiSortMeta.isEmpty())
    {
        for(SortMeta sortMeta:multiSortMeta)
        {
            if(sortMeta.getSortField().equalsIgnoreCase("stateId"))
            {
                orders.add(sortMeta.getSortOrder().equals(SortOrder.ASCENDING)?criteriaBuilder.asc(root.get(StateTable_.stateId)):criteriaBuilder.desc(root.get(StateTable_.stateId)));
            }
            else if(sortMeta.getSortField().equalsIgnoreCase("stateName"))
            {
                orders.add(sortMeta.getSortOrder().equals(SortOrder.ASCENDING)?criteriaBuilder.asc(root.get(StateTable_.stateName)):criteriaBuilder.desc(root.get(StateTable_.stateName)));
            }
            else if(sortMeta.getSortField().equalsIgnoreCase("country.countryName")) // Yes, Primefaces DataTable renders this ugly name in case of a nested property representing a foreign key relationship.
            {
                join = root.join(StateTable_.countryId, JoinType.INNER);
                orders.add(sortMeta.getSortOrder().equals(SortOrder.ASCENDING)?criteriaBuilder.asc(join.get(Country_.countryName)):criteriaBuilder.desc(join.get(Country_.countryName)));
            }
        }
    }

    //Filtering/searching

    List<Predicate>predicates=new ArrayList<Predicate>();

    if(filters!=null&&!filters.isEmpty())
    {
        for(Entry<String, String>entry:filters.entrySet())
        {
            if(entry.getKey().equalsIgnoreCase("stateId"))
            {
                predicates.add(criteriaBuilder.equal(root.get(StateTable_.stateId), Long.parseLong(entry.getValue())));
            }
            else if(entry.getKey().equalsIgnoreCase("stateName"))
            {
                predicates.add(criteriaBuilder.like(root.get(StateTable_.stateName), "%"+entry.getValue()+"%"));
            }
            else if(entry.getKey().equalsIgnoreCase("country.countryName"))// Yes, Primefaces DataTable renders this ugly name in case of a nested property representing a foreign key relationship.
            {
                if(join==null)
                {
                    join = root.join(StateTable_.countryId, JoinType.INNER);
                }
                predicates.add(criteriaBuilder.like(join.get(Country_.countryName), "%"+entry.getValue()+"%"));
            }
        }
    }

    if(predicates!=null&&!predicates.isEmpty())
    {
        criteriaQuery.where(predicates.toArray(new Predicate[0]));
    }

    if(orders!=null&&!orders.isEmpty())
    {
        criteriaQuery.orderBy(orders);
    }
    else
    {
        criteriaQuery.orderBy(criteriaBuilder.desc(root.get(StateTable_.stateId)));
    }
    TypedQuery<StateTable> typedQuery = entityManager.createQuery(criteriaQuery).setFirstResult(first).setMaxResults(pageSize);
    return typedQuery.getResultList();        
}

これは期待どおりに機能しますが、お気づきのように、データベース テーブル内の列の数が増えると、ループif-else if内のはしごに多くの条件チェックが含まれる可能性があります。foreach

各列には、並べ替えと検索の両方の条件チェックが必要です。if-else ifこのはしごを最終的に削除するか、少なくとも最小限に抑えることができるこれらの条件付きチェックを取り除く効率的な方法はありますか?

PS国の場合は、ではなくcountryName(親テーブルで利用可能な)でソートと検索を行っています。したがって、この場合は を使用しています。countrycountryIdJoin

4

1 に答える 1

5

値の使用を削除SingularAttributeし、呼び出し元がソート/フィルター フィールドで目的の列名を正確に使用してメソッドを呼び出すようにする場合は、反復されたソート/フィルター フィールドを列名として再利用するだけで、はるかに単純化できます。正しい列名を指定するために、フィールドの if/else チェックが必要です (実際には、並べ替え/フィルター フィールド名と同じです)。

基本的に、はしごでこれらのequalsIgnoreCase()チェックはまったく必要ありません。if-else大文字と小文字の区別については、呼び出し元が間違っている場合は、呼び出し元の間違いを許すのではなく、その場で修正してください。

それをリファクタリングする方法は次のとおりです。

/**
 * @throws NullPointerException When <code>multiSortMeta</code> or <code>filters</code> argument is null.
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public List<?> getList(int first, int pageSize, List<SortMeta> multiSortMeta, Map<String, String> filters) {
    // ...

    Root<StateTable> root = criteriaQuery.from(entityType);
    Join<StateTable, Country> join = root.join(StateTable_.countryId, JoinType.INNER);

    List<Order> orders = new ArrayList<Order>();

    for (SortMeta sortMeta : multiSortMeta) {
        String[] sortField = sortMeta.getSortField().split("\\.", 2);
        Path<Object> path = sortField.length == 1 ? root.get(sortField[0]) : join.get(sortField[1]);
        orders.add(sortMeta.getSortOrder() == SortOrder.ASCENDING 
            ? criteriaBuilder.asc(path) 
            : criteriaBuilder.desc(path));
    }

    List<Predicate>predicates = new ArrayList<Predicate>();

    for (Entry<String, String> filter : filters.entrySet()) {
        String[] filterField = filter.getKey().split("\\.", 2);
        Path path = filterField.length == 1 ? root.get(filterField[0]): join.get(filterField[1]);
        predicates.add(filter.getValue().matches("[0-9]+") 
            ? criteriaBuilder.equal(path, Long.valueOf(filter.getValue()))
            : criteriaBuilder.like(path, "%" + filter.getValue() + "%"));
    }

    // ...
}

nullまた、これらの null チェックをすべて安全に削除できるように、並べ替えおよびフィルターのメタとして受け入れないようにメソッドを変更したことに注意してください。for空の場合、ループはとにかく繰り返されないため、これらの空のチェックは不要です。また、フィルタリングではCriteriaBuilder#equal()、数値入力が指定された場合は が使用され、それ以外の場合は が使用されることに注意してくださいlike()。それがすべてのケースをカバーしているかどうかはわかりませんが、さらに微調整することをお勧めします。

必要に応じPathて、次のヘルパー メソッドを使用して、さらに多くの取得をリファクタリングできます。

@SuppressWarnings("rawtypes")
private static Path<?> getPath(String field, Root root, Join join) {
    String[] fields = field.split("\\.", 2);
    return fields.length == 1 ? root.get(fields[0]): join.get(fields[1]);
}
于 2013-08-30T20:23:22.693 に答える