gpt4 book ai didi

java - 在基于 LazyDataModel 的排序/过滤字段创建 JPA 条件查询时摆脱 if-else 阶梯

转载 作者:塔克拉玛干 更新时间:2023-11-02 07:49:39 26 4
gpt4 key购买 nike

我在用,

  • 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 中, <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 中获取排序和过滤后的行列表的代码如下。

@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梯子?

附言如果是国家/地区,我正在对 countryName 进行排序和搜索(在父表 country 中可用)而不是 countryId .因此,我正在使用 Join , 在这种情况下。

最佳答案

如果您放弃使用 SingularAttribute 值,并确保调用方在排序/过滤字段中使用完全所需的列名称调用该方法,那么您可以通过以下方式进一步简化它重复使用迭代的排序/过滤字段作为列名,而不需要对该字段进行 if/else 检查以指定正确的列名(毕竟它实际上与排序/过滤字段名相同)。

本质上,您根本不需要在 if-else 阶梯中进行那些 equalsIgnoreCase() 检查。至于区分大小写,如果调用者做错了,就在那里修正它,而不是对调用者的错误过于宽容。

下面是重构它的方法:

/**
* @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 作为排序和过滤元,这样您就可以安全地删除所有这些空检查。这些空检查是不必要的,因为 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]);
}

关于java - 在基于 LazyDataModel 的排序/过滤字段创建 JPA 条件查询时摆脱 if-else 阶梯,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17261124/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com