github:https://github.com/peterowang/spring-data-jpa-demo
Spring Data JPA已经帮助我们很大程度上简化了我们的查询操作,我们甚至只要写一个接口,然后单纯的写一些方法就可以完成各式各样的查询,但是对于我们程序设计人员而言,总希望所有的查询变得更加的简单方便,为了给程序人员进行再一次的封装,Spring Data JPA提供了Specification的方式进行查询,在前面的内容已经演示过这种查询了,但是,我们在使用的过程中发现这种查询异常的繁琐和复杂,接下来的内容就是我们有效的对Specification进行封装来快速实现一些简单的查询操作。当然如果涉及到更为复杂的操作,依然建议写个方法来自己实现。
封装自己的Specification的实现有很多种方法,我这里只给出了相对简单的一种,而且并没有考虑太复杂的查询,个人感觉过于复杂的查询还不如直接使用SQL或者HQL来处理方便,以下是几个比较重要的类:
SpecificationOperator
表示操作符类,用来确定查询条件和值。
package com.example.demo.SpecificationUtil; /** * Created by BFD-593 on 2017/8/17. * 操作符类,这个类中存储了键值对和操作符号,另外存储了连接下一个条件的类型是and还是or * 创建时通过 id>=7,其中id就是key,>=就是oper操作符,7就是value * 特殊的自定义几个操作符(:表示like %v%,l:表示v%,:l表示%v) */public class SpecificationOperator { /** * 操作符的key,如查询时的name,id之类 */ private String key; /** * 操作符的value,具体要查询的值 */ private Object value; /** * 操作符,自己定义的一组操作符,用来方便查询 */ private String oper; /** * 连接的方式:and或者or */ private String join; public SpecificationOperator(String key, Object value, String oper, String join) { this.key = key; this.value = value; this.oper = oper; this.join = join; } public SpecificationOperator() { } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public String getOper() { return oper; } public void setOper(String oper) { this.oper = oper; } public String getJoin() { return join; } public void setJoin(String join) { this.join = join; }}接下来创建SimpleSpecification
来实现Specification
接口,并且根据条件生成Specification
对象,因为在最后查询的时候需要这个对象
package com.example.demo.SpecificationUtil; import org.apache.commons.lang3.StringUtils;import org.springframework.data.jpa.domain.Specification; import javax.persistence.criteria.CriteriaBuilder;import javax.persistence.criteria.CriteriaQuery;import javax.persistence.criteria.Predicate;import javax.persistence.criteria.Root;import java.util.List; /** * 创建SimpleSpecification来实现Specification接口, * 并且根据条件生成Specification对象,因为在最后查询的时候需要这个对象 * SimpleSpecification是核心类型, * 用来根据条件生成Specification对象,这个SimpleSpecification直接存储了具体的查询条件。 * Created by BFD-593 on 2017/8/17. */public class SimpleSpecification<T> implements Specification<T> { /** * 查询的条件列表,是一组列表 * */ private List<SpecificationOperator> opers; public SimpleSpecification(List<SpecificationOperator> opers){ this.opers=opers; } @Override public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { int index = 0; Predicate resultPre = null; for(SpecificationOperator so :opers){ if(index++==0){//第一次index=0 index++是先赋值再加 resultPre = generatePredicate(root, criteriaBuilder, so); continue; } Predicate pre = generatePredicate(root, criteriaBuilder, so); if(pre==null)continue; if("and".equalsIgnoreCase(so.getJoin())){ resultPre = criteriaBuilder.and(resultPre, pre); }else if("or".equalsIgnoreCase(so.getJoin())){ resultPre = criteriaBuilder.or(resultPre, pre); } } return resultPre; } private Predicate generatePredicate(Root<T> root,CriteriaBuilder criteriaBuilder,SpecificationOperator so){ if(so!=null&&StringUtils.isNotEmpty(so.getOper())){ if("=".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){ return criteriaBuilder.equal(root.get(so.getKey()), so.getValue()); }else if(">=".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){ return criteriaBuilder.ge(root.get(so.getKey()).as(Number.class),(Number) so.getValue()); }else if("<=".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){ return criteriaBuilder.le(root.get(so.getKey()).as(Number.class),(Number)so.getValue()); }else if(">".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){ return criteriaBuilder.gt(root.get(so.getKey()).as(Number.class), (Number) so.getValue()); }else if("<".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){ return criteriaBuilder.lt(root.get(so.getKey()).as(Number.class), (Number) so.getValue()); }else if(":".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){ return criteriaBuilder.like(root.get(so.getKey()).as(String.class), "%" + so.getValue() + "%"); }else if(":l".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){ return criteriaBuilder.like(root.get(so.getKey()).as(String.class), "%" + so.getValue()); }else if("l:".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){ return criteriaBuilder.like(root.get(so.getKey()).as(String.class), so.getValue() + "%"); }else if("null".equalsIgnoreCase(so.getOper())){ return criteriaBuilder.isNull(root.get(so.getKey())); }else if("!null".equalsIgnoreCase(so.getOper())){ return criteriaBuilder.isNotNull(root.get(so.getKey())); }else if("!=".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){ return criteriaBuilder.notEqual(root.get(so.getKey()), so.getValue()); } } return null; }}
SimpleSpecification
是核心类型,用来根据条件生成Specification对象,这个SimpleSpecification
直接存储了具体的查询条件。
最后我们创建一个SimpleSpecificationBuilder
来具体创建SimpleSpecification
,这里为了方便调用简单进行了一下设计。
package com.example.demo.SpecificationUtil; import com.google.common.collect.Lists;import org.springframework.data.jpa.domain.Specification; import java.util.List; /** * 创建一个SimpleSpecificationBuilder来具体创建SimpleSpecification, * 这里为了方便调用简单进行了一下设计。 * Created by BFD-593 on 2017/8/17. */public class SimpleSpecificationBuilder<T> { /** * 条件列表 */ private List<SpecificationOperator> opers; /** * 构造函数,初始化的条件是and */ public SimpleSpecificationBuilder(String key,String oper,Object value,String join){ SpecificationOperator so = new SpecificationOperator(key, value, oper, join); opers = Lists.newArrayList(); opers.add(so); } /** * 构造,初始化无条件 */ public SimpleSpecificationBuilder(){ opers = Lists.newArrayList(); } /** * 往list中填加条件 * @param key * @param oper * @param value * @param join * @return */ public SimpleSpecificationBuilder add(String key,String oper,Object value,String join){ SpecificationOperator so = new SpecificationOperator(key, value, oper, join); opers.add(so); return this; } /** * 填加一个and条件 * @param key * @param oper * @param value * @return */ public SimpleSpecificationBuilder and(String key,String oper,Object value){ return this.add(key, oper, value, "and"); } /** * 填加一个or条件 * @param key * @param oper * @param value * @return */ public SimpleSpecificationBuilder or(String key,String oper,Object value){ return this.add(key, oper, value, "or"); } /** * 触发SimpleSpecification并返回Specification */ public Specification getSpecification(){ Specification<T> sp = new SimpleSpecification<T>(opers); return sp; }}测试:
/** * 在多条件动态查询时需要继承JpaSpecificationExecutor接口 * JpaSpecificationExecutor可以通过findAll方法传入SimpleSpecification来进行查询 * Created by BFD-593 on 2017/8/16. */public interface RoleRepository extends BaseRepository<Role,Integer>,JpaSpecificationExecutor<Role> { }
/** * 测试封装的specification * 实现简单风格的动态查询 * id < id and roleName like %roleName% or id>id and roleName like roleName%的动态查询 * 某个参数为空时,就不使用该参数所在的条件。 * @param roleName * @param id * @return */public List<Role> spe(String roleName,Integer id) { return roleRepository.findAll(new SimpleSpecificationBuilder<Role>(). and("id", "<", id). and("roleName",":",roleName). or("id",">",id). and("roleName","l:",roleName). getSpecification());}
时间: 2024-10-29 19:06:21