Spring Data JPA中使用Specification<T>实现动态查询

首先, 定义的Dao接口不仅要继承JpaRepository接口,还有继承JpaSpecificationExecutor接口, 如下:

//JpaRepository 用于简单查询
//JpaSpecificationExecutor 动态拼接sql 用于复杂查询
public interface LabelDao extends JpaRepository<Label,String> ,JpaSpecificationExecutor<Label> {
}

分析源码

按住Ctrl键 点击JpaSpecificationExecutor可以看到

public interface JpaSpecificationExecutor<T> {
    Optional<T> findOne(@Nullable Specification<T> var1);

    List<T> findAll(@Nullable Specification<T> var1);

    Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);

    List<T> findAll(@Nullable Specification<T> var1, Sort var2);

    long count(@Nullable Specification<T> var1);
}

通过方法名我们大体可以知道:

//根据条件查询一个对象
Optional findOne(@Nullable Specification var1);

//根据条件查询集合
List findAll(@Nullable Specification var1);

//根据条件分页查询
Page findAll(@Nullable Specification var1, Pageable var2);

//排序查询查询
List findAll(@Nullable Specification var1, Sort var2);

//统计查询
long count(@Nullable Specification var1);

这时我有点疑惑,Specification是什么,好吧,点进去看一下
再次按住Ctrl键 点击Specification

public interface Specification<T> extends Serializable {
    long serialVersionUID = 1L;

    static <T> Specification<T> not(Specification<T> spec) {
        return Specifications.negated(spec);
    }

    static <T> Specification<T> where(Specification<T> spec) {
        return Specifications.where(spec);
    }

    default Specification<T> and(Specification<T> other) {
        return Specifications.composed(this, other, CompositionType.AND);
    }

    default Specification<T> or(Specification<T> other) {
        return Specifications.composed(this, other, CompositionType.OR);
    }

    @Nullable
    Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
}

最后这个方法就是我们动态查询要实现的方法了,分析:

Predicate toPredicate(Root var1, CriteriaQuery<?> var2, CriteriaBuilder var3);

var1: 代表查询的根对象,可以通过 var1获取实体中的属性
var2:代表一个顶层查询对象,用来自定义查询
var3:用于添加查询条件,此对象里有很多条件方法

分析完了,就看怎么写代码来实现动态查询吧

//这里我的前端给我传的是参数是一个map
private Specification<Label> createSpecification(Map map) {

//匿名内部类,实现接口方法
        Specification<Label> spec = new Specification() {
        
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery cq, CriteriaBuilder cb) {
            
            //创建集合,用于存放拼接的条件
                List<Predicate> list = new ArrayList<>();
                
                if(map.get("labelname")!=null&&!"".equals(map.get("labelname"))){
                    Predicate p1 = cb.like(root.get("labelname").as(String.class), "%" + map.get("labelname") + "%");
                    list.add(p1);
                }
                if(map.get("state")!=null&&!"".equals(map.get("state"))){
                    Predicate p2 = cb.equal(root.get("state").as(String.class), map.get("state"));
                    list.add(p2);
                }
                if(map.get("recommend")!=null&&!"".equals(map.get("recommend"))){
                    Predicate p3 = cb.equal(root.get("recommend").as(String.class), map.get("recommend"));
                    list.add(p3);
                }
                return cb.and(list.toArray(new Predicate[list.size()]));
            }
        };
        return spec;
    }

这样就可以轻松实现多条件动态查询 和 多条件动态查询加分页了

//多条件查询
    public List<Label> findSearch(Map map){
        Specification<Label> spec = createSpecification(map);
        return labelDao.findAll(spec);
    }
//多条件加分页查询
    public Page<Label> findSearch(Map map, int page, int size){
        Specification<Label> spec = createSpecification(map);
        Pageable pageable = PageRequest.of(page - 1, size);
        return labelDao.findAll(spec,pageable);
    }

因为sql拼接是共用的代码,我把它分离出来封装成方法,这个肯定一眼就能看出来吧…

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值