github:https://github.com/peterowang/spring-data-jpa-demo

单一实体的动态查询:

@Servicepublic class AdvancedUserInfoService{    @Autowired    UserInfoRepository userInfoRepository;    /**     * 简单分页排序查询     */    public Page<UserInfo> pageList(int pageNo,int pageSize){        Sort sort = new Sort(Sort.Direction.DESC, "id");        Pageable able = new PageRequest(pageNo, pageSize, sort);        return userInfoRepository.findAll(able);    }

    /**     * 复杂动态多条件查询     * @param username     * @param password     * @param id     * @return     */    public List<UserInfo> listDynamic(final String username,final String password,final Integer id){        Specification<UserInfo> sf = new Specification<UserInfo>() {            List<Predicate> list = new ArrayList<>();            @Override            public Predicate toPredicate(Root<UserInfo> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {/*              Predicate p1 = cb.like(root.get("name").as(String.class), "%"+um.getName()+"%");                Predicate p2 = cb.equal(root.get("uuid").as(Integer.class), um.getUuid());                Predicate p3 = cb.gt(root.get("age").as(Integer.class), um.getAge());                //把Predicate应用到CriteriaQuery中去,因为还可以给CriteriaQuery添加其他的功能,比如排序、分组啥的                query.where(cb.and(p3,cb.or(p1,p2)));//where p3 and (p1 or p2)                //添加排序的功能                query.orderBy(cb.desc(root.get("uuid").as(Integer.class)));                return query.getRestriction();*/                List<Predicate> list = new ArrayList<>();                if(!StringUtils.isEmpty(username)){                    list.add(criteriaBuilder.like(root.get("username").as(String.class), "%" + username + "%"));                }                if(!StringUtils.isEmpty(password)){                    list.add(criteriaBuilder.isNotNull(root.get("password").as(String.class)));                }                if(id!=null){                    list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("id").as(Integer.class),id));                }                Predicate[] pd = new Predicate[list.size()];                criteriaQuery.where(list.toArray(pd));                criteriaQuery.orderBy(criteriaBuilder.desc(root.get("id").as(Integer.class)));                return criteriaQuery.getRestriction();            }        } ;        return userInfoRepository.findAll(sf);    }    public Page<UserInfo> pageDynamic(final String username,final String password,final Integer id1,                                      final Integer id2,final Integer pageNo,final Integer pageSize){        return userInfoRepository.findAll(new Specification() {            @Override            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {                Predicate p1=null;Predicate p2=null; Predicate p3=null;                if(StringUtils.isNotEmpty(username)){                    p1 = criteriaBuilder.equal(root.get("username").as(String.class),username);                }                if(StringUtils.isNotEmpty(password)){                    p2 = criteriaBuilder.equal(root.get("password").as(String.class), password);                }                if(id1!=null&&id2!=null){                    p3 = criteriaBuilder.between(root.get("id").as(Integer.class), id1, id2);                }                criteriaQuery.where(criteriaBuilder.and(p1,criteriaBuilder.or(p2,p3)));                return criteriaQuery.getRestriction();            }        },new PageRequest(pageNo,pageSize,new Sort(Sort.Direction.DESC,"id")));    }}

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());}

转载于:https://www.cnblogs.com/wangjing666/p/7383121.html

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

  1. Spring Data JPA 从入门到精通~@NamedQueries预定义查询

    这种是预定义查询的一种形式 (1)在 @Entity 下增加 @NamedQuery 定义. public @interface NamedQuery {//query的名称,规则:实体.方法名:St ...

  2. Spring data JPA -针对一对多关系中,分页查询一的一方部分,但查询条件中有多的一方参数

    jpa框架让大家快速实现增删查改,真是太方便了.但是针对以下这个场景大家需要注意一下,我们项目中真实用到的,记录下来分享给大家. Account 和AccountDetail 是一对多关系,正常针对A ...

  3. Spring Data JPA 从入门到精通~JpaSpecificationExecutor示例

    新建两个实体 @Entity(name = "UserInfoEntity") @Table(name = "user_info", schema = &quo ...

  4. Spring Data JPA 从入门到精通~JpaSpecificationExecutor的使用方法

    JpaSpecificationExecutor 源码和 API 我们也可以通过 idea 工具详细看其用法和实现类,JpaSpecificationExecutor 是 Repository 要继承 ...

  5. spring data jpa和mybatis的不同

    使用spring data JPA和mybatis都是用来访问数据库,但他们的分工不同: 1.spring data jpa默认使用hibernate作为ORM实现,是spring 提供的一套jpa接 ...

  6. Spring Data JPA简单学习

    从一个简单的 JPA 示例开始 本文主要讲述 Spring Data JPA,但是为了不至于给 JPA 和 Spring 的初学者造成较大的学习曲线,我们首先从 JPA 开始,简单介绍一个 JPA 示 ...

  7. springdatajpa命名规则_简单了解下spring data jpa

    公司准备搭建一个通用框架,以后项目就用统一一套框架了 以前只是听过jpa,但是没有实际用过 今天就来学习下一些简单的知识 什么是JPA 全称Java Persistence API,可以通过注解或者X ...

  8. spring data jpa从入门到精通_Spring Data JPA的简单入门

    前言 spring data JPA是spring团队打造的sping生态全家桶的一部分,本身内核使用的是hibernate核心源码,用来作为了解java持久层框架基本构成的样本是再好不过的选择.最近 ...

  9. Spring Data JPA简单使用

    Spring Data Jpa 1.JPA简介 JPA的出现主要是为了简化持久层开发以及整合ORM技术,结束Hibernate.TopLink.JDO等ORM框架各自为营的局面.JPA是在吸收现有OR ...

最新文章

  1. django2 mysql配置_Django:Python3.6.2+Django2.0配置MySQL 转载
  2. 高性能计时器Timer的设计(时间轮和时间堆两种方式)
  3. SeetaFace2 测试
  4. centos安装mysql密码_centos 安装mysql并设置密码
  5. 招聘职位:ERP咨询顾问(广州、厦门)
  6. 代码命名,代码里的命名规则:错误的和正确的对比 命名方法总结 “自我描述的源代码”用代码表达出你的思想,让其他人通过代码能明白你的意图。...
  7. 流程DEMO-合同会审表
  8. [Think]故事几则
  9. 小猿圈Linux 之用户和用户组管理(二)
  10. 虚拟仿真引擎消息机制
  11. 科技公司都是如何应对 COVID-19?
  12. C语言中指针与数组的区别,C语言 指针与数组的详解及区别
  13. httpd配置ResponseHeader
  14. 分享一些 Windows 平台上的神器
  15. MAC安装Securecrt
  16. oracle月份相减函数,Oracle 日期函数的加减
  17. 【Qt+ OpenGL】实现人体3D显示与控制
  18. oppo手机怎么投射到电脑屏幕上
  19. android端的声音检测程序(检测声音分贝大小)
  20. sap erp 消息服务器,九慧信息|SAP_SAP ERP_SAP系统_SAP s/4hana

热门文章

  1. 很火的仿soul交友盲盒1.0全开源源码
  2. 2019年7月第一周总结-RabbitMQ总结
  3. Express是Node.js上最流行的Web开发框架。
  4. 漫步ASP.NET MVC的处理管线
  5. 网站开发技巧参考大全 event js
  6. jQuery: 判断指针是否在某元素内 How do I check if the mouse is over an element
  7. 用ssh反向连接访问内网主机 ( 实例使用autossh隧道实现mysql的同步 )
  8. Ubuntu: 创建PlayOnLinux快捷键 Create PlayOnLinux Application Desktop
  9. 自定义字体 (暂不支持中文)
  10. jQuery插件Label Effect制作个性化的文字特效