Tin*_*iny 3 java criteria-api primefaces jsf-2 jpa-2.0
我正在使用,
我在MySQL数据库中有一个state_table以三列命名的表作为示例.
state_id是一个自动生成的主键,country_id是一个引用country表的主键的外键.
该表由其对应的实体类映射,StateTable该表持有的数据显示在Primefaces DataTable中<p:dataTable>...</p:dataTable>.
所述DataTable列标题中包含一个可点击的排序区,<div>用于与用于分选,当该区域被点击时,一个字符串,可以是排序方向的每一列ASCENDING或DESCENDING表示排序顺序呈现和用于其中用户输入滤波(搜索)的文本框每列的搜索项.
所以最终,我在JSF托管bean中获得的是一个类型List,java.util.List<org.primefaces.model.SortMeta>表示DataTable用户希望的列的排序顺序.
并且将java.util.Map<java.lang.String, java.lang.String>表示搜索列的类型的映射名称作为键并将相应列的搜索项作为值(用户在每列的列标题上的文本框中输入搜索项DataTable).
简而言之,我List<SortMeta>用于排序和Map<String, String>过滤/搜索.
在排序和过滤后获取行列表的其中一个DAO中的代码如下所示.
@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();
}
Run Code Online (Sandbox Code Playgroud)
这可以按预期工作,但是可以注意到,循环if-else if内的梯形图foreach可以包含许多条件检查,因为数据库表中的列数增加了.
每列都需要对排序和搜索进行条件检查.是否有一种有效的方法来摆脱这些有条件的检查,最终可以删除或至少最小化这个if-else if阶梯?
PS如果是国家,我正在进行排序和搜索countryName(在父表中可用country)而不是countryId.因此,Join在这种情况下,我正在使用.
如果删除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() + "%"));
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
请注意,我还将方法修改为不接受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]);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2014 次 |
| 最近记录: |