Springboot中对jpa动态查询条件的封装

来源:互联网 发布:淘宝商品网页无法显示 编辑:程序博客网 时间:2024/06/05 22:36

jpa对于固定参数的条件查询比较简单,可以在Repository中直接用参数名来查询。但是对于不固定的参数查询就比较麻烦了,官方提供的是继承JpaSpecificationExecutor,然后自己拼接Specification。这一篇主要是对Specification进行封装,让写法更友好.
代码参考:http://lee1177.iteye.com/blog/1994295。感觉还不够完整,回头使用中再补上。

一:自定义Specification

创建条件表达式接口,模拟系统的条件查询

import javax.persistence.criteria.CriteriaBuilder;import javax.persistence.criteria.CriteriaQuery;import javax.persistence.criteria.Predicate;import javax.persistence.criteria.Root;public interface Criterion {    enum Operator {        EQ, NE, LIKE, GT, LT, GTE, LTE, AND, OR, IS_MEMBER, IS_NOT_MEMBER    }    Predicate toPredicate(Root<?> root, CriteriaQuery<?> query,                          CriteriaBuilder builder);}

创建一个自定义的Sepcification,添加add方法用来添加多个条件

import javax.persistence.criteria.CriteriaBuilder;import javax.persistence.criteria.CriteriaQuery;import javax.persistence.criteria.Predicate;import javax.persistence.criteria.Root;import java.util.ArrayList;import java.util.List;/** * Created by 定义一个查询条件容器  on 17/6/6. */public class Criteria<T> implements Specification<T> {    private List<Criterion> criterions = new ArrayList<>();    @Override    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,                                 CriteriaBuilder builder) {        if (!criterions.isEmpty()) {            List<Predicate> predicates = new ArrayList<>();            for (Criterion c : criterions) {                predicates.add(c.toPredicate(root, query, builder));            }            // 将所有条件用 and 联合起来            if (predicates.size() > 0) {                return builder.and(predicates.toArray(new Predicate[predicates.size()]));            }        }        return builder.conjunction();    }    /**     * 增加简单条件表达式     *     * @Methods Name add     * @Create In 2012-2-8 By lee     */    public void add(Criterion criterion) {        if (criterion != null) {            criterions.add(criterion);        }    }}

二:创建条件表达式接口的不同实现类

import org.springframework.util.StringUtils;import javax.persistence.criteria.*;import java.util.List;import java.util.Map;import java.util.Set;/** * 简单条件表达式 * * @author lee */public class SimpleExpression implements Criterion {    /**     *  属性名     */    private String fieldName;    /**     *  对应值     */    private Object value;    /**     * 计算符     */    private Operator operator;    protected SimpleExpression(String fieldName, Object value, Operator operator) {        this.fieldName = fieldName;        this.value = value;        this.operator = operator;    }    @Override    @SuppressWarnings({"rawtypes", "unchecked"})    public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query,                                 CriteriaBuilder builder) {        Path expression;        //此处是表关联数据,注意仅限一层关联,如user.address,        //查询user的address集合中,address的name为某个值        if (fieldName.contains(".")) {            String[] names = StringUtils.split(fieldName, ".");            //获取该属性的类型,Set?List?Map?            expression = root.get(names[0]);            Class clazz = expression.getJavaType();            if (clazz.equals(Set.class)) {                SetJoin setJoin = root.joinSet(names[0]);                expression = setJoin.get(names[1]);            } else if (clazz.equals(List.class)) {                ListJoin listJoin = root.joinList(names[0]);                expression = listJoin.get(names[1]);            } else if (clazz.equals(Map.class)) {                MapJoin mapJoin = root.joinMap(names[0]);                expression = mapJoin.get(names[1]);            } else {                //是many to one时                expression = expression.get(names[1]);            }        } else {            //单表查询            expression = root.get(fieldName);        }        switch (operator) {            case EQ:                return builder.equal(expression, value);            case NE:                return builder.notEqual(expression, value);            case LIKE:                return builder.like((Expression<String>) expression, "%" + value + "%");            case LT:                return builder.lessThan(expression, (Comparable) value);            case GT:                return builder.greaterThan(expression, (Comparable) value);            case LTE:                return builder.lessThanOrEqualTo(expression, (Comparable) value);            case GTE:                return builder.greaterThanOrEqualTo(expression, (Comparable) value);            case IS_MEMBER:                return builder.isMember(value, expression);            case IS_NOT_MEMBER:                return builder.isNotMember(value, expression);            default:                return null;        }    }}
import javax.persistence.criteria.CriteriaBuilder;import javax.persistence.criteria.CriteriaQuery;import javax.persistence.criteria.Predicate;import javax.persistence.criteria.Root;import java.util.ArrayList;import java.util.List;/** * 逻辑条件表达式 用于复杂条件时使用,如单属性多对应值的OR查询等 * * @author lee */public class LogicalExpression implements Criterion {    /**     * 逻辑表达式中包含的表达式     */    private Criterion[] criterion;    /**     * 计算符     */    private Operator operator;    public LogicalExpression(Criterion[] criterions, Operator operator) {        this.criterion = criterions;        this.operator = operator;    }    @Override    public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query,                                 CriteriaBuilder builder) {        List<Predicate> predicates = new ArrayList<>();        for (int i = 0; i < this.criterion.length; i++) {            predicates.add(this.criterion[i].toPredicate(root, query, builder));        }        switch (operator) {            case OR:                return builder.or(predicates.toArray(new Predicate[predicates.size()]));            default:                return null;        }    }}

这两个类分别模拟不同的条件查询。

三:创建一个工厂类,根据条件创建不同的实现类

import org.springframework.util.StringUtils;import java.util.Collection;/** * 条件构造器 * 用于创建条件表达式 * * @Class Name Restrictions * @Author lee */public class Restrictions {    /**     * 等于     */    public static SimpleExpression eq(String fieldName, Object value, boolean ignoreNull) {        if (ignoreNull && StringUtils.isEmpty(value)) {            return null;        }        return new SimpleExpression(fieldName, value, Criterion.Operator.EQ);    }    /**     * 集合包含某个元素     */    public static SimpleExpression hasMember(String fieldName, Object value, boolean ignoreNull) {        if (ignoreNull && StringUtils.isEmpty(value)) {            return null;        }        return new SimpleExpression(fieldName, value, Criterion.Operator.IS_MEMBER);    }    /**     * 不等于     */    public static SimpleExpression ne(String fieldName, Object value, boolean ignoreNull) {        if (ignoreNull && StringUtils.isEmpty(value)) {            return null;        }        return new SimpleExpression(fieldName, value, Criterion.Operator.NE);    }    /**     * 模糊匹配     */    public static SimpleExpression like(String fieldName, String value, boolean ignoreNull) {        if (ignoreNull && StringUtils.isEmpty(value)) {            return null;        }        return new SimpleExpression(fieldName, value, Criterion.Operator.LIKE);    }    /**     *///    public static SimpleExpression like(String fieldName, String value,//                                        MatchMode matchMode, boolean ignoreNull) {//        if (StringUtils.isEmpty(value)) return null;//        return null;//    }    /**     * 大于     */    public static SimpleExpression gt(String fieldName, Object value, boolean ignoreNull) {        if (ignoreNull && StringUtils.isEmpty(value)) {            return null;        }        return new SimpleExpression(fieldName, value, Criterion.Operator.GT);    }    /**     * 小于     */    public static SimpleExpression lt(String fieldName, Object value, boolean ignoreNull) {        if (ignoreNull && StringUtils.isEmpty(value)) {            return null;        }        return new SimpleExpression(fieldName, value, Criterion.Operator.LT);    }    /**     * 小于等于     */    public static SimpleExpression lte(String fieldName, Object value, boolean ignoreNull) {        if (ignoreNull && StringUtils.isEmpty(value)) {            return null;        }        return new SimpleExpression(fieldName, value, Criterion.Operator.GTE);    }    /**     * 大于等于     */    public static SimpleExpression gte(String fieldName, Object value, boolean ignoreNull) {        if (ignoreNull && StringUtils.isEmpty(value)) {            return null;        }        return new SimpleExpression(fieldName, value, Criterion.Operator.LTE);    }    /**     * 并且     */    public static LogicalExpression and(Criterion... criterions) {        return new LogicalExpression(criterions, Criterion.Operator.AND);    }    /**     * 或者     */    public static LogicalExpression or(Criterion... criterions) {        return new LogicalExpression(criterions, Criterion.Operator.OR);    }    /**     * 包含于     */    @SuppressWarnings("rawtypes")    public static LogicalExpression in(String fieldName, Collection value, boolean ignoreNull) {        if (ignoreNull && (value == null || value.isEmpty())) {            return null;        }        SimpleExpression[] ses = new SimpleExpression[value.size()];        int i = 0;        for (Object obj : value) {            ses[i] = new SimpleExpression(fieldName, obj, Criterion.Operator.EQ);            i++;        }        return new LogicalExpression(ses, Criterion.Operator.OR);    }      /**     * 集合包含某几个元素,譬如可以查询User类中Set<String> set包含"ABC","bcd"的User集合,     * 或者查询User中Set<Address>的Address的name为"北京"的所有User集合     * 集合可以为基本类型或者JavaBean,可以是one to many或者是@ElementCollection     * @param fieldName     * 列名     * @param value     * 集合     * @return     * expresssion     */    public static LogicalExpression hasMembers(String fieldName, Object... value) {        SimpleExpression[] ses = new SimpleExpression[value.length];        int i = 0;        //集合中对象是基本类型,如Set<Long>,List<String>        Criterion.Operator operator = Criterion.Operator.IS_MEMBER;        //集合中对象是JavaBean        if (fieldName.contains(".")) {            operator = Criterion.Operator.EQ;        }        for (Object obj : value) {            ses[i] = new SimpleExpression(fieldName, obj, operator);            i++;        }        return new LogicalExpression(ses, Criterion.Operator.OR);    }}

四:使用

假设有个Post的entity,有title,content,count,url等参数,再创建一个PostRepository extends JpaRepository

    @Test    public void contextLoads() {        Criteria<Post> criteria = new Criteria<>();        criteria.add(Restrictions.like("title", "1", true));        criteria.add(Restrictions.eq("content", "content1", true));        List<Post> postList = postRepository.findAll(criteria);        for (Post post : postList) {            System.out.println(post);        }    }

这里就可以比较优雅的创建不同的条件,然后拼接起来查询即可。相比之下要比原生的写法如

    studentInfoDao.findAll(new Specification<StudentInfo> () {     public Predicate toPredicate(Root<StudentInfo> root,       CriteriaQuery<?> query, CriteriaBuilder cb) {      Path<String> namePath = root.get("name");      Path<String> nicknamePath = root.get("nickname");      /**          * 连接查询条件, 不定参数,可以连接0..N个查询条件          */      query.where(cb.like(namePath, "%李%"), cb.like(nicknamePath, "%王%")); //这里可以设置任意条查询条件      return null;     }    }, page);   }  

比这种写法好看一点。

原创粉丝点击