JPA -> Specification 封装通用方法,解决代码繁琐的烦恼

第一步:定义sql 连接符枚举和sql 条件枚举

package com.miaomiao.common.enums;

/**
 * @program: miaomiao
 * @description:
 * @author: lzy
 * @create: 2020-11-03 09:56
 */
public enum  SqlConnectEnum {
    /**
     * sql 链接属性 and
     */
    AND,

    /**
     * sql 链接属性 or
     */
    OR;

    SqlConnectEnum() {
    }

    public static SqlConnectEnum fromString(String value) {
        return valueOf(value.toLowerCase());
    }
}
package com.miaomiao.common.enums;

/**
 * @program: miaomiao
 * @description:
 * @author: lzy
 * @create: 2020-10-28 19:31
 */
public enum SqlOperateEnum {
    /**
     * sql 条件属性 =
     */
    EQ,

    /**
     * sql 条件属性 !=
     */
    NE,

    /**
     * sql 条件属性 >
     */
    GT,
    /**
     * sql 条件属性 <
     */
    LT,
    /**
     * sql 条件属性 =
     */
    GE,
    /**
     * sql 条件属性 =
     */
    LE,
    /**
     * sql 条件属性 like
     */
    LIKE,
    /**
     * sql 条件属性 in
     */
    IN,
    /**
     * sql 条件属性 为空
     */
    ISNULL,
    /**
     * sql 条件属性 不为空
     */
    ISNOTNULL;

    SqlOperateEnum() {
    }

    public static SqlOperateEnum fromString(String value) {
        return valueOf(value.toLowerCase());

    }
}

第二步:定义自定查询实体

package com.miaomiao.common.sql;

import com.miaomiao.common.enums.SqlConnectEnum;
import com.miaomiao.common.enums.SqlOperateEnum;

import java.io.Serializable;

/**
 * @program: miaomiao
 * @description:
 * @author: lzy
 * @create: 2020-11-02 17:06
 */
public class SqlFilter implements Serializable {
    private static final long serialVersionUID = 1L;
    private String property;
    private Object value;
    private SqlConnectEnum sqlConnectEnum;
    private SqlOperateEnum sqlOperateEnum;



    public SqlFilter() {
    }

    public SqlFilter(SqlConnectEnum sqlConnectEnum, String property, SqlOperateEnum sqlOperateEnum, Object value) {
        this.sqlConnectEnum = sqlConnectEnum;
        this.property = property;
        this.sqlOperateEnum = sqlOperateEnum;
        this.value = value;

    }

    public static SqlFilter andEq(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.AND, property, SqlOperateEnum.EQ, value);
    }
    public static SqlFilter orEq(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.OR, property, SqlOperateEnum.EQ, value);
    }

    public static SqlFilter andNe(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.AND, property, SqlOperateEnum.NE, value);
    }
    public static SqlFilter orNe(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.OR, property, SqlOperateEnum.NE, value);
    }

    public static SqlFilter andGt(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.AND, property, SqlOperateEnum.GT, value);
    }
    public static SqlFilter orGt(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.OR, property, SqlOperateEnum.GT, value);
    }

    public static SqlFilter andLt(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.AND, property, SqlOperateEnum.LT, value);
    }
    public static SqlFilter orLt(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.OR, property, SqlOperateEnum.LT, value);
    }

    public static SqlFilter andGe(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.AND, property, SqlOperateEnum.GE, value);
    }
    public static SqlFilter orGe(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.OR, property, SqlOperateEnum.GE, value);
    }

    public static SqlFilter andLe(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.AND, property, SqlOperateEnum.LE, value);
    }
    public static SqlFilter orLe(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.OR, property, SqlOperateEnum.LE, value);
    }

    public static SqlFilter andLike(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.AND, property, SqlOperateEnum.LIKE, value);
    }
    public static SqlFilter orLike(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.OR, property, SqlOperateEnum.LIKE, value);
    }

    public static SqlFilter andIn(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.AND, property, SqlOperateEnum.IN, value);
    }
    public static SqlFilter orIn(String property, Object value) {
        return new SqlFilter(SqlConnectEnum.OR, property, SqlOperateEnum.IN, value);
    }

    public static SqlFilter andIsNull(String property) {
        return new SqlFilter(SqlConnectEnum.AND, property, SqlOperateEnum.ISNULL, (Object)null);
    }
    public static SqlFilter orIsNull(String property) {
        return new SqlFilter(SqlConnectEnum.OR, property, SqlOperateEnum.ISNULL, (Object)null);
    }

    public static SqlFilter andIsNotNull(String property) {
        return new SqlFilter(SqlConnectEnum.OR, property, SqlOperateEnum.ISNOTNULL, (Object)null);
    }
    public static SqlFilter orIsNotNull(String property) {
        return new SqlFilter(SqlConnectEnum.OR, property, SqlOperateEnum.ISNOTNULL, (Object)null);
    }

    public String getProperty() {
        return property;
    }

    public void setProperty(String property) {
        this.property = property;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    public SqlConnectEnum getSqlConnectEnum() {
        return sqlConnectEnum;
    }

    public void setSqlConnectEnum(SqlConnectEnum sqlConnectEnum) {
        this.sqlConnectEnum = sqlConnectEnum;
    }

    public SqlOperateEnum getSqlOperateEnum() {
        return sqlOperateEnum;
    }

    public void setSqlOperateEnum(SqlOperateEnum sqlOperateEnum) {
        this.sqlOperateEnum = sqlOperateEnum;
    }
}

第三步:在基础实体对象通过继承sqlHelp类转成specification对象方法

package com.miaomiao.common.model;

/**
 * BasePO
 *
 * @author lzy
 */

public class BasePo extends SqlHelp {
    /**
     * 获取按实体参数进行查询的specification
     * @Param 无
     * return specification
     */
    public Specification toSpecification() {
        return getSpecification(this);
    }

    /**
     * 获取按过滤参数和实体参数进行查询的specification
     * @Param sqlFilter
     * return specification
     */
    public Specification toSpecification(SqlFilter sqlFilter) {
        List list = Arrays.asList(sqlFilter);
        return toSpecification(list);
    }

    /**
     * 获取按过滤参数进行查询的specification
     * @Param sqlFilters
     * return specification
     */
    public Specification toSpecification(List sqlFilters) {
        return getSpecificationBySqlFilter(this, sqlFilters);
    }
}

第四步:编写SqlHelp解析类

package com.miaomiao.common.sql;

import com.miaomiao.common.enums.SqlConnectEnum;
import com.miaomiao.common.model.BasePo;
import com.miaomiao.common.utils.SqlUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @program: miaomiao
 * @description:
 * @author: lzy
 * @create: 2020-11-04 11:18
 */
public class SqlHelp {
    /**
     * 根据sqlFilters封装Specification
     * @Param basePo 基础实体类
     * @Param sqlFilters 过滤条件集合
     * return 处理结果
     */
    public Specification getSpecificationBySqlFilter(BasePo basePo, List sqlFilters) {
        // sqlFilters为空,执行无过滤查询
        if (CollectionUtils.isEmpty(sqlFilters)){
            return getSpecification(basePo);
        }
        return (Specification) (root, criteriaQuery, criteriaBuilder) -> {
            // 获取所有类属性
            List fields = SqlUtils.getAllField(basePo.getClass());
            // 定义and或者or数组
            List andList = new ArrayList();
            List orList = new ArrayList();
            // 过滤掉自定义参数字段
            List filterFields = SqlUtils.getFilterField(fields, sqlFilters);
            // 循环实体类属性字段查询
            for (Field field : filterFields) {
                try {
                    field.setAccessible(true);
                    Path path = root.get(field.getName());
                    // 获得属性的值
                    Object value = field.get(basePo);
                    if (value == null) {
                        continue;
                    }
                    andList.add(criteriaBuilder.and(criteriaBuilder.equal(path, value)));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

            // 循环过滤参数
            for (SqlFilter sqlFilter : sqlFilters) {
                Path path = root.get(sqlFilter.getProperty());
                // 获得属性的值
                Object value = sqlFilter.getValue();
                if (value == null) {
                    continue;
                }
                // 判断sql查询符
                switch(sqlFilter.getSqlOperateEnum()){
                    case EQ:
                        if (sqlFilter.getSqlConnectEnum() == SqlConnectEnum.AND){
                            andList.add(criteriaBuilder.and(criteriaBuilder.equal(path, value)));
                        }
                        if (sqlFilter.getSqlConnectEnum() == SqlConnectEnum.OR){
                            orList.add(criteriaBuilder.or(criteriaBuilder.equal(path, value)));
                        }
                        break;
                    case NE:
                        if (sqlFilter.getSqlConnectEnum() == SqlConnectEnum.AND){
                            andList.add(criteriaBuilder.and(criteriaBuilder.notEqual(path, value)));
                        }
                        if (sqlFilter.getSqlConnectEnum() == SqlConnectEnum.OR){
                            orList.add(criteriaBuilder.or(criteriaBuilder.notEqual(path, value)));
                        }
                        break;
                    case IN:
                        if (sqlFilter.getSqlConnectEnum() == SqlConnectEnum.AND){
                            CriteriaBuilder.In in = criteriaBuilder.in(path);
                            if(value instanceof List){
                                for (Object o : (List) value) {
                                    in.value(o);
                                }
                            }
                            andList.add(criteriaBuilder.and(in));
                        }
                        if (sqlFilter.getSqlConnectEnum() == SqlConnectEnum.OR){
                            CriteriaBuilder.In in = criteriaBuilder.in(path);
                            if(value instanceof List){
                                for (Object o : (List) value) {
                                    in.value(o);
                                }
                            }
                            orList.add(criteriaBuilder.or(in));
                        }
                        break;
                    case LIKE:
                        if (sqlFilter.getSqlConnectEnum() == SqlConnectEnum.AND){
                            andList.add(criteriaBuilder.and(criteriaBuilder.like(path.as(String.class), "%" + value + "%")));
                        }
                        if (sqlFilter.getSqlConnectEnum() == SqlConnectEnum.OR){
                            orList.add(criteriaBuilder.or(criteriaBuilder.like(path.as(String.class), "%" + value + "%")));
                        }
                        break;
                    default:
                }
            }
            // 转换成predicate
            Predicate andPredicate = criteriaBuilder.and(andList.toArray(new Predicate[0]));
            Predicate orPredicate = criteriaBuilder.or(orList.toArray(new Predicate[0]));
            return criteriaQuery.where(andPredicate,orPredicate).getRestriction();
        };
    }

    /**
     * 根据封装Specification
     * @Param basePo 基础实体类
     * return 处理结果
     */
    public Specification getSpecification(BasePo basePo) {
        List fields = SqlUtils.getAllField(basePo.getClass());
        return (Specification) (root, criteriaQuery, criteriaBuilder) -> {
            List predicates = new ArrayList();
            for (Field field : fields) {
                field.setAccessible(true);
                try {
                    Path path = root.get(field.getName());
                    // 获得属性的值
                    Object value = field.get(basePo);
                    if (value == null) {
                        continue;
                    }
                    predicates.add(criteriaBuilder.equal(path, value));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            // and连接
            Predicate predicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
            return predicate;
        };
    }
}

 第五步:编写SqlHelp工具类

package com.miaomiao.common.utils;

import com.alibaba.fastjson.JSON;
import com.miaomiao.common.model.BasePO;
import com.miaomiao.common.sql.SqlFilter;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @program: miaomiao
 * @description:
 * @author: lzy
 * @create: 2020-11-02 17:25
 */
public class SqlUtils {

    /**
     *  找到类所有字段包括父类的集合
     * @param clazz 类Class
     * @return 类所有字段的集合
     */
    public static List getAllField(Class clazz) {
        List fieldList = new ArrayList<>();
        Field[] fields = clazz.getDeclaredFields();
        if (fields.length != 0) {
            fieldList.addAll(Arrays.asList(fields));
        }
        Class superclass = clazz.getSuperclass();
        // 如果父类是Object, 直接返回
        if (superclass == Object.class) {
            return fieldList;
        }
        // 递归获取所有的父级的Field
        List superClassFieldList = getAllField(superclass);
        if (!superClassFieldList.isEmpty()) {
            superClassFieldList.stream()
                // 去除重复字段
                .filter(field -> !fieldList.contains(field))
                .forEach(fieldList::add);
        }
        return fieldList;
    }

    /**
     * 过滤Field
     * @Param sqlFilters
     * @Param fields
     * return 处理结果
     */
    public static List getFilterField(List fields, List sqlFilters){
        // 获取SqlFilter所有属性字段
        List fieldList = sqlFilters.stream().map(SqlFilter::getProperty).distinct().collect(Collectors.toList());
        Iterator iterator = fields.iterator();
        while (iterator.hasNext()){
            Field field = iterator.next();
            field.setAccessible(true);
            if (fieldList.contains(field.getName())){
                iterator.remove();
            }
        }
        return fields;
    }
}

第六步:PO要继承基础Po类,并且要实现泛型(不可缺漏)

JPA -> Specification 封装通用方法,解决代码繁琐的烦恼_第1张图片

第五步:用法和效果

JPA -> Specification 封装通用方法,解决代码繁琐的烦恼_第2张图片

总结:

反射真香!

你可能感兴趣的:(喵喵中台建设,反射,java,jpa)