Java for Web学习笔记(一二零):搜索(2)JPA的动态条件搜索(中)

151人阅读 评论(0) 收藏 举报
分类:

通用的检索

定义可以获得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相关文章

查看评论

级联动态搜索条件表单的创建

1、serivce类: @Service public class EffectService { @Autowired private FindInfoClassDao findInf...
  • snow1wolf
  • snow1wolf
  • 2014-11-24 12:00:48
  • 560

Java for Web学习笔记(一二一):搜索(3)JPA的动态条件搜索(下)

例子的具体实现仓库的实现使用spring data,增加自定义接口SearchableRepository,具体如下:public interface PersonRepository extends...
  • flowingflying
  • flowingflying
  • 2018-04-15 20:53:27
  • 154

Web条件搜索

阔别多日,又想起了博客,写点今天的东西,发现如果长时间不记录的话,学的东西很容易就会忘掉,还是把这个任务记录到博客里面来的痛快,少废话,看代码。...
  • low_down
  • low_down
  • 2016-11-02 00:01:13
  • 141

JavaWeb不定条件查询

JavaWeb不定条件查询的
  • Canpliy
  • Canpliy
  • 2016-08-29 21:05:29
  • 2745

java web中的多条件查询

所谓多条件查询即为用户输入想要查询的条件,然后根据用户输入的条件进行查询。      当用户有可能什么也不输入,这个条件我们应该考虑到。如果不输入就查询,则我们在页面上显示所有的查询结果,    ...
  • xulu_258
  • xulu_258
  • 2015-06-24 16:14:27
  • 8800

jquery 多条件搜索特效 高级查询

  • 2015年11月23日 17:33
  • 126KB
  • 下载

jdbc中查找条件动态化

一般一个select语句的查找条件具有不确定性,如果单纯的只是把查找条件本身当做参数放入查找函数中,就不具有灵活性,如下:public Goddess queryOne(Integer id) thr...
  • u014473112
  • u014473112
  • 2017-02-22 16:45:03
  • 254

Web自动化框架LazyUI使用手册(2)--先跑起来再说(第一个测试用例-百度搜索)

作者:cryanimal QQ:164166060上篇文章中,简要介绍了LazyUI框架,本文便来演示,如何从无到有快速搭建基于lazyUI的工程,并成功运行第一个测试用例。本文以百度搜索为例,选用c...
  • kaka1121
  • kaka1121
  • 2016-06-14 19:21:15
  • 2709

Spring Data JPA 多条件查询

Spring Data JPA 查询很方便,但做搜索功能时,条件都是未知的,并不能用方法命名的方式查询,自己写JPQL,用表达式的方式处理也不靠谱,条件多了简直就是作死,以前写过下面的代码: SELE...
  • wncnke
  • wncnke
  • 2017-01-13 14:37:56
  • 12202
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 497万+
    积分: 5万+
    排名: 58
    博客专栏
    我的公告
    我思故我在,如果一个人无法独立思考,即使熟习各种工具,实质只是工具的一部分,永远无法创造工具。
    文章转载只能用于非商业性质,且不能带有虚拟货币、积分等附加条件;转载必须注明出处。

    我的微博帐号:@恺风Wei
    最新评论
    文章存档