在基于LazyDataModel的排序/过滤字段创建JPA条件查询时,删除if-else梯形图
我正在使用,
- JPA 2.0
- Mojarra 2.1.9
- JSF组件库,Primefaces 3.5。
- MySQL 5.6.11
我在MySQL数据库中有一个名为state_table
的表,以三列为例。
- state_id(BigInt)
- state_name(Varchar)
- country_id(BigInt)
state_id
是自动生成的主键, country_id
是引用country
表的主键的外键。
该表由其对应的名为StateTable
实体类映射,该表持有的数据显示在Primefaces DataTable
,
...
。
DataTable
列标题包含一个可单击的排序区域,每个列的
DESCENDING
或DESCENDING
以及用于过滤的文本框(搜索)其中用户输入每列的搜索项。
因此,最终,我在JSF托管bean中得到的是一个类型为java.util.List
的List,表示用户希望的DataTable
列的排序顺序。
并且类型为java.util.Map
表示搜索列名称作为键,并将相应列的搜索项作为值(搜索项由用户输入DataTable
每列的列标题上的文本框)。
简而言之,我使用List
进行排序,使用Map
进行过滤/搜索。
在排序和过滤后获取行列表的其中一个DAO中的代码如下所示。
@Override @SuppressWarnings("unchecked") public List getList(int first, int pageSize, List multiSortMeta, Mapfilters) { CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(StateTable.class); Metamodel metamodel=entityManager.getMetamodel(); EntityType entityType = metamodel.entity(StateTable.class); Rootroot=criteriaQuery.from(entityType); Join join = null; //Sorting List orders=new ArrayList(); 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 Listpredicates=new ArrayList(); if(filters!=null&&!filters.isEmpty()) { for(Entryentry: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 typedQuery = entityManager.createQuery(criteriaQuery).setFirstResult(first).setMaxResults(pageSize); return typedQuery.getResultList(); }
这可以按预期工作,但是可以注意到, foreach
循环中的if-else if
梯形图可以包含许多条件检查,因为数据库表中的列数增加了。
每列都需要对排序和搜索进行条件检查。 是否有一种有效的方法可以摆脱这些条件检查,最终可以删除或至少最小化if-else if
梯形图?
PS如果是国家/地区,我正在对countryName
(在父表country
可用)而不是countryId
进行排序和搜索。 因此,在这种情况下,我正在使用Join
。
如果删除SingularAttribute
值的使用并确保调用者在排序/filter字段中使用所需的列名称调用该方法,那么只需重复使用迭代排序/filter字段作为列名称即可简化它不需要对字段进行if / else检查以指定正确的列名(实际上它与排序/filter字段名称完全相同)。
基本上,你根本不需要那些equalsIgnoreCase()
检查if-else
梯形图。 对于区分大小写,如果调用者做错了,只需将其修复到那里,而不是对调用者的错误过于宽容。
这是你如何重构它然后:
/** * @throws NullPointerException When
multiSortMeta
orfilters
argument is null. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public List> getList(int first, int pageSize, ListmultiSortMeta, Map filters) { // ... Root root = criteriaQuery.from(entityType); Join join = root.join(StateTable_.countryId, JoinType.INNER); List orders = new ArrayList (); for (SortMeta sortMeta : multiSortMeta) { String[] sortField = sortMeta.getSortField().split("\\.", 2); Path
请注意,我还将方法修改为不接受null
作为排序和过滤元,以便您可以安全地删除所有这些空检查。 那些空检查是不必要的,因为如果for
循环为空, 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]); }
- 使用JPA标准的“不在”约束
- JPA Criteria API – 如何添加JOIN子句(作为一般句子)
- Criteria API:按类类型过滤
- JPA / Hibernate静态元模型属性未填充 – NullPointerException
- 使用JPA Criteria Api和hibernate spatial 4
- 如何在某些单元格上使用CriteriaQuery SUM自定义操作?
- 如何在JPA critera API中做一个独特的计数?
- JPA 2 – 在CriteriaQuery中使用@ElementCollection
- 使用JPA Criteria API,您是否可以执行只获得一次连接的获取连接?