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

 #本人封装的,方便编写动态语句
 <dependency>
     <groupId>top.openyuan</groupId>
     <artifactId>jpa-plus</artifactId>
     <version>1.0.0</version>
 </dependency>

第一步:定义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 team.miaomiao.common.specification;

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

import java.io.Serializable;

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

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

    }

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

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

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

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

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

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

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

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

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

    public static MmPredicate andIsNotNull(String property) {
        return new MmPredicate(SqlConnectEnum.OR, property, SqlOperateEnum.ISNOTNULL, (Object)null);
    }
    public static MmPredicate orIsNotNull(String property) {
        return new MmPredicate(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;
    }
}

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

package team.miaomiao.common.model;

import com.google.common.collect.Lists;
import team.miaomiao.common.specification.MmPredicate;
import team.miaomiao.common.specification.MmSpecificatication;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.format.annotation.DateTimeFormat;

import javax.persistence.*;
import java.util.*;

/**
 * BasePo
 *
 * @author lzy
 */
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BasePo<T> extends MmSpecificatication<T> {

    /**
     * 获取按实体参数进行查询的specification
     * @Param 无
     * @return specification
     */
    public Specification<T> toSpecification(){
        return objectToSpecification(this);
    }

    /**
     * 获取按过滤参数和实体参数进行查询的specification
     * @Param sqlFilter
     * @return specification
     */
    public Specification<T> toSpecification(MmPredicate... sqlFilter) {
        List<MmPredicate> list = Lists.newArrayList(sqlFilter);
        return toSpecification(list);
    }

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

第四步:编写MmSpecificatication解析类

package team.miaomiao.common.specification;

import com.google.common.collect.Lists;
import team.miaomiao.common.enums.SqlConnectEnum;
import team.miaomiao.common.utils.ReflectionUtils;
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.List;
import java.util.Objects;

/**
 * @program: miaomiao
 * @description:
 * @author: lzy
 * @create: 2020-11-04 11:18
 */
public class MmSpecificatication<T> {
    /**
     * 根据sqlFilters封装Specification
     * @Param basePo 基础实体类
     * @Param sqlFilters 过滤条件集合
     * return 处理结果
     */
    public Specification<T> sqlFilterToSpecification(Object beanParam, List<MmPredicate> predicates) {
        // sqlFilters为空,执行无过滤查询
        if (CollectionUtils.isEmpty(predicates)){
            return objectToSpecification(beanParam);
        }
        return (Specification<T>) (root, criteriaQuery, criteriaBuilder) -> {
            // 获取所有类属性
            List<Field> fields = ReflectionUtils.getAllField(beanParam.getClass());
            // 定义and或者or数组
            List<Predicate> andList = Lists.newArrayList();
            List<Predicate> orList = Lists.newArrayList();
            // 过滤掉自定义参数字段
            List<Field> filterFields = ReflectionUtils.getFilterField(fields, predicates);
            // 循环实体类属性字段查询
            filterFields.stream().forEach(item ->{
                try {
                    item.setAccessible(true);
                    Path<Object> path = root.get(item.getName());
                    // 获得属性的值
                    Object value = item.get(beanParam);
                    if (Objects.isNull(value)) {
                        return;
                    }
                    andList.add(criteriaBuilder.and(criteriaBuilder.equal(path, value)));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            });
            // 循环过滤参数
            predicates.stream().forEach(item ->{
                Path<Object> path = root.get(item.getProperty());
                // 获得属性的值
                Object value = item.getValue();
                if (Objects.isNull(value)) {
                    return;
                }
                // 判断sql查询符
                switch(item.getSqlOperateEnum()){
                    case EQ:
                        if (item.getSqlConnectEnum() == SqlConnectEnum.AND){
                            andList.add(criteriaBuilder.and(criteriaBuilder.equal(path, value)));
                        }
                        if (item.getSqlConnectEnum() == SqlConnectEnum.OR){
                            orList.add(criteriaBuilder.or(criteriaBuilder.equal(path, value)));
                        }
                        break;
                    case NE:
                        if (item.getSqlConnectEnum() == SqlConnectEnum.AND){
                            andList.add(criteriaBuilder.and(criteriaBuilder.notEqual(path, value)));
                        }
                        if (item.getSqlConnectEnum() == SqlConnectEnum.OR){
                            orList.add(criteriaBuilder.or(criteriaBuilder.notEqual(path, value)));
                        }
                        break;
                    case IN:
                        if (item.getSqlConnectEnum() == SqlConnectEnum.AND){
                            CriteriaBuilder.In<Object> in = criteriaBuilder.in(path);
                            if(value instanceof List<?>){
                                for (Object o : (List<?>) value) {
                                    in.value(o);
                                }
                            }
                            andList.add(criteriaBuilder.and(in));
                        }
                        if (item.getSqlConnectEnum() == SqlConnectEnum.OR){
                            CriteriaBuilder.In<Object> 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 (item.getSqlConnectEnum() == SqlConnectEnum.AND){
                            andList.add(criteriaBuilder.and(criteriaBuilder.like(path.as(String.class), "%" + value + "%")));
                        }
                        if (item.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<T> objectToSpecification(Object beanParam) {
        List<Field> fields = ReflectionUtils.getAllField(beanParam.getClass());
        return (Specification<T>) (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = Lists.newArrayList();
            fields.stream().forEach(field ->{
                field.setAccessible(true);
                try {
                    Path<Object> path = root.get(field.getName());
                    // 获得属性的值
                    Object value = field.get(beanParam);
                    if (Objects.isNull(value)) {
                        return;
                    }
                    predicates.add(criteriaBuilder.equal(path, value));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            });
            // and连接
            Predicate predicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
            return predicate;
        };
    }
}

 第五步:编写ReflectionUtils 工具类

package team.miaomiao.common.utils;

import team.miaomiao.common.specification.MmPredicate;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

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

    /**
     *  找到类所有字段包括父类的集合
     * @param clazz 类Class
     * @return 类所有字段的集合
     */
    public static List<Field> getAllField(Class<?> clazz) {
        List<Field> 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<Field> superClassFieldList = getAllField(superclass);
        if (superClassFieldList.isEmpty()) {
            return fieldList;
        }
        superClassFieldList.stream()
            // 去除重复字段
            .filter(field -> !fieldList.contains(field))
            .forEach(fieldList::add);
        return fieldList;
    }

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

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

第五步:用法和效果

总结:

反射真香!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值