序
本文主要研究下jpa的动态查询
javax.persistence.criteria
jpa从hibernate里头吸收了criteria,利用criteria结合对url查询语法的解析,也可以实现端到端的动态查询。
下面展示下springside branch 4版本中的实现。
springside branch 4
SearchFilter
/*******************************************************************************
* Copyright (c) 2005, 2014 springside.github.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
*******************************************************************************/
package org.springside.modules.persistence;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Maps;
public class SearchFilter {
public enum Operator {
EQ, LIKE, GT, LT, GTE, LTE
}
public String fieldName;
public Object value;
public Operator operator;
public SearchFilter(String fieldName, Operator operator, Object value) {
this.fieldName = fieldName;
this.value = value;
this.operator = operator;
}
/**
* searchParams中key的格式为OPERATOR_FIELDNAME
*/
public static Map parse(Map searchParams) {
Map filters = Maps.newHashMap();
for (Entry entry : searchParams.entrySet()) {
// 过滤掉空值
String key = entry.getKey();
Object value = entry.getValue();
if (StringUtils.isBlank((String) value)) {
continue;
}
// 拆分operator与filedAttribute
String[] names = StringUtils.split(key, "_");
if (names.length != 2) {
throw new IllegalArgumentException(key + " is not a valid search filter name");
}
String filedName = names[1];
Operator operator = Operator.valueOf(names[0]);
// 创建searchFilter
SearchFilter filter = new SearchFilter(filedName, operator, value);
filters.put(key, filter);
}
return filters;
}
}
这里定义了几种运算符EQ, LIKE, GT, LT, GTE, LTE
可以自己从mvc的controller中接收查询条件转换为SearchFilter
DynamicSpecifications
/*******************************************************************************
* Copyright (c) 2005, 2014 springside.github.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
*******************************************************************************/
package org.springside.modules.persistence;
import java.util.Collection;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springside.modules.utils.Collections3;
import com.google.common.collect.Lists;
public class DynamicSpecifications {
public static Specification bySearchFilter(final Collection filters, final Class entityClazz) {
return new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder builder) {
if (Collections3.isNotEmpty(filters)) {
List predicates = Lists.newArrayList();
for (SearchFilter filter : filters) {
// nested path translate, 如Task的名为"user.name"的filedName, 转换为Task.user.name属性
String[] names = StringUtils.split(filter.fieldName, ".");
Path expression = root.get(names[0]);
for (int i = 1; i < names.length; i++) {
expression = expression.get(names[i]);
}
// logic operator
switch (filter.operator) {
case EQ:
predicates.add(builder.equal(expression, filter.value));
break;
case LIKE:
predicates.add(builder.like(expression, "%" + filter.value + "%"));
break;
case GT:
predicates.add(builder.greaterThan(expression, (Comparable) filter.value));
break;
case LT:
predicates.add(builder.lessThan(expression, (Comparable) filter.value));
break;
case GTE:
predicates.add(builder.greaterThanOrEqualTo(expression, (Comparable) filter.value));
break;
case LTE:
predicates.add(builder.lessThanOrEqualTo(expression, (Comparable) filter.value));
break;
}
}
// 将所有条件用 and 联合起来
if (!predicates.isEmpty()) {
return builder.and(predicates.toArray(new Predicate[predicates.size()]));
}
}
return builder.conjunction();
}
};
}
}
这里主要是将SearchFilter构造的查询条件转换为对应的Predicate,进而构造成jpa的Specification,来完成动态查询条件的转换。
小结
使用springside的DynamicSpecifications,再把mvc的参数映射为SearchFilter,也可以自己实现一套端到端的动态查询。
备注springside在最新版本已经删除掉这些代码,得在branch 4分支查找。
doc
- SearchFilter
- DynamicSpecifications
- 使用RSQL实现端到端的动态查询