spring data jpa封装specification实现简单风格的动态查询

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

spring data jpa封装specification实现简单风格的动态查询的相关文章

Spring data jpa中Query和@Query分别返回map结果集

引用: http://blog.csdn.net/yingxiake/article/details/51016234 http://blog.csdn.net/yingxiake/article/details/51016234 http://www.cnblogs.com/zj0208/p/6008627.html Query的使用: 在JPA 2.0 中我们可以使用entityManager.createNativeQuery()来执行原生的SQL语句. 但当我们查询结果没有对应实体类时,

Spring data jpa 实现简单动态查询的通用Specification方法

本篇前提: SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法 这篇文章中的第二种方法 实现Specification 这块的方法 只适用于一个对象针对某一个固定字段查询,下面通过泛型改写了这个方法: import java.util.List; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import j

SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法

首先谢谢大佬的简书文章:http://www.jianshu.com/p/45ad65690e33# 这篇文章中讲的是spring中使用spring data jpa,使用了xml配置文件.我现在使用的是spring boot ,没有了xml文件配置就方便多了.我同样尝试了两种方式,也都是简单的查询,需要更复杂的查询,还需要我研究研究.往下看,需要先配置springboot的开发环境,需要大致了解springboot,这里可以看下面两篇文章: springboot 项目新建 springboot

spring data jpa的动态查询封装(转)

最近使用spring data jpa做了两个项目,对于动态查询的不友好做了个类似hibernate的封装,记录也分享下 首先定义一个所有条件的容器,继承Specification Java代码   /** * 定义一个查询条件容器 * @author lee * * @param <T> */ public class Criteria<T> implements Specification<T>{ private List<Criterion> crit

spring data jpa 的简单使用

先说简单一下JPA 概念:JPA(Java Persistence API)是Sun官方提出的Java持久化规范.它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据. 影响:他的出现主要是为了简化现有的持久化开发工作和整合ORM技术,结束现在Hibernate,TopLink,JDO等ORM框架各自为营的局面 进入正题 Spring DATA JPA 1.概念:它是基于ORM框架.JPA规范封装的一套JPA应用框架可使开发者用极简的代码即可实现对数据的访问和操作,它提

Spring Data JPA、 MyBatis与Hibernate简单对比

整体参考: https://blog.csdn.net/xihuanyuye/article/details/81201441 详细参考: https://blog.csdn.net/qq897958555/article/details/53208002 1.Spring Data JPA与Hibernate Spring Data JPA是Spring Data的子模块.使用Spring Data,使得基于“repositories”概念的JPA实现更简单和容易.Spring Data JP

SpringMVC+Spring Data JPA+Shiro+EasyUI简单权限管理系统

SpringMVC+Spring Data JPA+Shiro+EasyUI简单权限管理系统 一直想做一个管理系统,希望它简洁,能做一个demo使用.以后在研究学习的时候,可以在此基础上增加代码.我觉得权限管理系统很值得做,因为涉及关系数据库模式的设计,能学到很多东西.万事开头难,先做个简单的,以后再慢慢完善的.任何事情关键是要做,不能停留在想. 前端 由于之前没有多少前端编程经验,所以做起前端比较吃力.之前前端使用Bootstrap,发现需要自己编写很多前端代码,虽然花费了很多时间,但是页面做

Spring Data JPA Specification Query

private Specification<VO> createSpecification(final Map<String, String> filterData) throws Exception { final Map<String, String> filterOperators = new HashMap<>(); final Map<String, Object> filterParams = new HashMap(); extra

spring data jpa 创建方法名进行简单查询

Spring Data JPA框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find.findBy.read.readBy.get.getBy,然后对剩下部分进行解析. 假如创建如下的查询:findByTaskProjectName(),框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,假设查询实体为Doc 1.先判断 taskProjectName (根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询:如果没有该属