Java for Web学习笔记(一二零):搜索(2)JPA的动态条件搜索(中)
程序员文章站
2022-04-22 13:38:22
...
通用的检索
定义可以获得Predicate[]的SearchCriteria
SearchCriteria: 单个条件Criterion的集合
SearchCriteria是单个条件Criterion的集合List<Criterion>
/*代码很简单,也很有趣。由于List<T>是一个接口,必须有具体的实现,例子采用了ArrayList<T>,但我们希望只开放List接口的方法。*/
public interface SearchCriteria extends List<Criterion>{
public static class Builder {
public static SearchCriteria create(){
return new SearchCriteriaImpl();
}
private static class SearchCriteriaImpl extends ArrayList<Criterion> implements SearchCriteria{
private static final long serialVersionUID = 1L;
}
}
}
单个条件Criterion
public class Criterion {
//【1】单个条件有三个有要素:
// 1.1)对象的那个属性(对应数据库表格哪个列)
private final String propertyName;
// 1.2)是什么操作,等于,大于还是什么。这个操作在Enum Operator中定义,具体见后面
private final Operator operator;
// 1.3)和什么对比,也就是等于大于什么。因为该对象属性的类型不知道,所以采用了Object
private final Object compareTo;
public Criterion(String propertyName, Operator operator, Object compareTo) {
this.propertyName = propertyName;
this.operator = operator;
this.compareTo = compareTo;
}
public String getPropertyName() {
return propertyName;
}
public Operator getOperator() {
return operator;
}
public Object getCompareTo() {
return compareTo;
}
//【2】具体定义操作,包括EQ,NEQ,LT等等。比较特别地,我们也同时给出了这些操作如何转换为JPA中的Predicate。
// 这种在enum中带有方法的处理,我们以往很少见到。
public static enum Operator{
EQ{ //【3.1】这是判断相等的操作。注意:我们这里不是比较object的地址,而是判断值是否一样,因此需要确保对象是Compatable的。我们通过一个静态方法getComparable()来实现,如果非compatable的,直接抛出异常。
@Override
public Predicate toPredicate(Criterion c, Root<?> r, CriteriaBuilder b) {
return b.equal(r.get(c.propertyName), getComparable(c));
}
}, NEQ { ...// 如上类似 b.notEqual(r.get(c.getPropertyName()), getComparable(c));
}, LT {//【3.2】原则上lessThan和equal没有什么区别,但是在输入参数类型上有要求。如果我们给出一个通用的Compatable<?>,出现报错,修订为下面的方式。
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public Predicate toPredicate(Criterion c, Root<?> r, CriteriaBuilder b) {
return b.lessThan(r.<Comparable>get(c.getPropertyName()), getComparable(c));
}
}, LTE { ...// 如上类似 b.lessThanOrEqualTo(r.<Comparable>get(c.getPropertyName()), getComparable(c))
}, GT { ...// 如上类似 b.greaterThan(r.<Comparable>get(c.getPropertyName()), getComparable(c))
}, GTE { ...// 如上类似 b.greaterThanOrEqualTo(r.<Comparable>get(c.getPropertyName()), getComparable(c))
}, LIKE { //【3.3】很显然LIKE只能是String,例如"%123"这类,使用getString()来获取,如果不是String,直接抛出异常
@Override
public Predicate toPredicate(Criterion c, Root<?> r, CriteriaBuilder b) {
return b.like(r.get(c.getPropertyName()), getString(c));
}
}, NOT_LIKE { ...// 如上类似 b.notLike(r.get(c.getPropertyName()), getString(c))
}, IN { //【3.4】IN的情况是一个Collection,进行类似的处理,只是直接写进来
@Override
public Predicate toPredicate(Criterion c, Root<?> r, CriteriaBuilder b) {
Object o = c.getCompareTo();
if (o == null)
return r.get(c.getPropertyName()).in();
if (o instanceof Collection)
return r.get(c.getPropertyName()).in((Collection<?>) o);
throw new IllegalArgumentException(c.getPropertyName());
}
}, NOT_IN {
@Override
public Predicate toPredicate(Criterion c, Root<?> r, CriteriaBuilder b) {
Object o = c.getCompareTo();
if (o == null)
return b.not(r.get(c.getPropertyName()).in());
if (o instanceof Collection)
return b.not(r.get(c.getPropertyName()).in((Collection<?>) o));
throw new IllegalArgumentException(c.getPropertyName());
}
}, NULL {
@Override
public Predicate toPredicate(Criterion c, Root<?> r, CriteriaBuilder b) {
return r.get(c.getPropertyName()).isNull();
}
}, NOT_NULL { ...// 如上类似 r.get(c.getPropertyName()).isNotNull()
};
//【3】注意这种写法,为Enum里面增加方法
public abstract Predicate toPredicate(Criterion c, Root<?> r, CriteriaBuilder b);
@SuppressWarnings("rawtypes")
private static Comparable getComparable(Criterion c){
Object o = c.getCompareTo();
if(o != null && !(o instanceof Comparable<?>))
throw new IllegalArgumentException(c.getPropertyName());
return (Comparable<?>) o;
}
private static String getString(Criterion c){
if(!(c.getCompareTo() instanceof String))
throw new IllegalArgumentException(c.getPropertyName());
return (String)c.getCompareTo();
}
}
}
提供复合条件
AbstractSearchableJpaRepository是我们提供的通用检索仓库,在后面介绍。
class AbstractSearchableJpaRepository{
....
private static Predicate[] toPredicates(SearchCriteria criteria, Root<?> root,CriteriaBuilder builder){
Predicate[] predicates = new Predicate[criteria.size()];
int i =0;
for(Criterion c : criteria)
predicates[i++] = c.getOperator().toPredicate(c, root, builder);
return predicates;
}
}
通用仓库
AbstractDomainClassAwareRepository :提供T的类型获取
这种方式,在之前我们写通用的CRUD时已经学过,复习一下abstract class AbstractDomainClassAwareRepository<T> {
protected final Class<T> domainClass;
@SuppressWarnings("unchecked")
protected AbstractDomainClassAwareRepository(){
Type genericSuperclass = this.getClass().getGenericSuperclass();
while(!(genericSuperclass instanceof ParameterizedType)){
if(!(genericSuperclass instanceof Class))
throw new IllegalStateException("Unable to determine type " +
"arguments because generic superclass neither " +
"parameterized type nor class.");
if(genericSuperclass == AbstractDomainClassAwareRepository.class)
throw new IllegalStateException("Unable to determine type " +
"arguments because no parameterized generic superclass " +
"found.");
genericSuperclass = ((Class<T>)genericSuperclass).getGenericSuperclass();
}
ParameterizedType type = (ParameterizedType)genericSuperclass;
Type[] arguments = type.getActualTypeArguments();
this.domainClass = (Class<T>)arguments[0];
}
}
SearchableRepository:通用多条件检索接口定义
public interface SearchableRepository<T> {
Page<T> search(SearchCriteria criteria, Pageable pageable);
}
AbstractSearchableJpaRepository:通用多条件检索实现
abstract class AbstractSearchableJpaRepository<T> extends AbstractDomainClassAwareRepository<T>
implements SearchableRepository<T>{
@PersistenceContext protected EntityManager entityManager;
@Override
public Page<T> search(SearchCriteria criteria, Pageable pageable) {
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
//根据Page<T>的实现PageImpl<>,需要有总数和响应范围的List,所有需要查询两次
// 查询了总个数
CriteriaQuery<Long> countCriteria = builder.createQuery(Long.class);
Root<T> countRoot = countCriteria.from(this.domainClass);
long total = this.entityManager.createQuery(
countCriteria.select(builder.count(countRoot))
.where(toPredicates(criteria, countRoot, builder))
).getSingleResult();
// 查询相关页的list
CriteriaQuery<T> pageCriteria = builder.createQuery(this.domainClass);
Root<T> pageRoot = pageCriteria.from(this.domainClass);
List<T> list = this.entityManager.createQuery(
pageCriteria.select(pageRoot)
.where(toPredicates(criteria, pageRoot, builder))
//QueryUtils.toOrders: turns a Spring Data Sort into JPA ordering instructions
.orderBy(QueryUtils.toOrders(pageable.getSort(), pageRoot, builder))
).setFirstResult(pageable.getOffset())
.setMaxResults(pageable.getPageSize())
.getResultList();
return new PageImpl<>(new ArrayList<>(list), pageable, total);
}
private static Predicate[] toPredicates(SearchCriteria criteria, Root<?> root,CriteriaBuilder builder){ .... ... }
}
相关链接:我的Professional Java for Web Applications相关文章上一篇: (转)文本溢出省略号……
下一篇: 文本溢出省略号