zoukankan      html  css  js  c++  java
  • spring data jpa封装specification实现简单风格的动态查询

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

    单一实体的动态查询:

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


  • 相关阅读:
    python--Pandas(一)
    Orcale(一)
    三、显示博客信息
    二、Django需要的知识点
    一、Django初级
    mysql修改密码
    python更新mysql数据
    python查询mysql数据
    python与mysql的连接过程
    python如果想输出原格式的内容,可以加''' ''',占位符使用方式
  • 原文地址:https://www.cnblogs.com/wangjing666/p/7383121.html
Copyright © 2011-2022 走看看