All Downloads are FREE. Search and download functionalities are using the official Maven repository.

top.openyuan.jpa.specification.handler.AbstractPredicateHandler Maven / Gradle / Ivy

package top.openyuan.jpa.specification.handler;

import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.openyuan.jpa.exception.BuildSpecificationException;
import top.openyuan.jpa.specification.handler.bean.BetweenValue;
import top.openyuan.jpa.specification.predicate.AbstractSimplePredicateExt;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Predicate.BooleanOperator;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * 用来动态构造 JPA 中 {@link Predicate} 的抽象类.
 *
 * @author lzy
 * @since v1.0.0
 */
public abstract class AbstractPredicateHandler implements PredicateHandler {

    private Logger logger = LoggerFactory.getLogger(AbstractPredicateHandler.class);

    /**
     * 获取对应注解类的 {@code Class} 类型的值.
     *
     * @return {@code Class} 类型的值
     */
    public abstract Class getAnnotation();

    /**
     * 构造 {@code AND} 关系的 {@link Predicate} 实例的方法.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param annotation 前字段使用的注解
     * @param  范型 Z
     * @param  范型 X
     * @return {@link Predicate} 实例
     */
    public abstract  Predicate buildPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value, Annotation annotation);

    /**
     * 构造 {@code AND} 关系的 {@link Predicate} 实例的方法.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 属性字段名称
     * @param value 属性条件对应的值
     * @return {@link Predicate} 实例
     */
    @Override
    public Predicate buildPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        return this.buildPredicate(criteriaBuilder, from, fieldName, value, null);
    }

    /**
     * 构造相等条件 {@code Equals} 的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildEqualsPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        return criteriaBuilder.equal(from.get(fieldName), value);
    }

    /**
     * 构造不相等条件 {@code Not Equals} 的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildNotEqualsPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        return criteriaBuilder.notEqual(from.get(fieldName), value);
    }

    /**
     * 构造大于查询的 {@link Predicate} 实例的方法.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 属性字段名称
     * @param value 属性条件对应的值
     * @param  范型 Z
     * @param  范型 X
     * @return {@link Predicate} 实例
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    protected  Predicate buildGreaterThanPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        this.isValueComparable(value);
        return criteriaBuilder.greaterThan(from.get(fieldName), (Comparable) value);
    }

    /**
     * 构造大于等于查询的 {@link Predicate} 实例的方法.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 属性字段名称
     * @param value 属性条件对应的值
     * @param  范型 Z
     * @param  范型 X
     * @return {@link Predicate} 实例
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    protected  Predicate buildGreaterThanEqualPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        this.isValueComparable(value);
        return criteriaBuilder.greaterThanOrEqualTo(from.get(fieldName), (Comparable) value);
    }

    /**
     * 构造小于查询的 {@link Predicate} 实例的方法.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 属性字段名称
     * @param value 属性条件对应的值
     * @param  范型 Z
     * @param  范型 X
     * @return {@link Predicate} 实例
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    protected  Predicate buildLessThanPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        this.isValueComparable(value);
        return criteriaBuilder.lessThan(from.get(fieldName), (Comparable) value);
    }

    /**
     * 构造小于等于查询的 {@link Predicate} 实例的方法.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 属性字段名称
     * @param value 属性条件对应的值
     * @param  范型 Z
     * @param  范型 X
     * @return {@link Predicate} 实例
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    protected  Predicate buildLessThanEqualPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        this.isValueComparable(value);
        return criteriaBuilder.lessThanOrEqualTo(from.get(fieldName), (Comparable) value);
    }

    /**
     * 检查值是否实现了 {@link Comparable} 接口.
     *
     * @param value 值
     */
    private void isValueComparable(Object value) {
        if (!(value instanceof Comparable)) {
            throw new BuildSpecificationException("【Jpa-plus 异常】要比较的 value 值【" + value + "】不是可比较类型的,"
                    + "该值的类型必须实现了 java.lang.Comparable 接口才能正常参与比较,才能用于大于、大于等于、小于、小于等于之类的比较场景.");
        }
    }

    /**
     * 构造空条件 {@code IS NULL} 的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildIsNullPredicate(CriteriaBuilder criteriaBuilder, From from, Object value) {
        return criteriaBuilder.isNull(from.get(String.valueOf(value)));
    }

    /**
     * 构造不是空条件 {@code IS NOT NULL} 的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildIsNotNullPredicate(
            CriteriaBuilder criteriaBuilder, From from, Object value) {
        return criteriaBuilder.isNotNull(from.get(String.valueOf(value)));
    }

    /**
     * 构造相等条件 {@code Equals} 的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param allowNull 是否允许 null 值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildInPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value, boolean allowNull) {
        value = value.getClass().isArray() ? Arrays.asList((Object[]) value) : value;
        Path path = from.get(fieldName);
        CriteriaBuilder.In in = criteriaBuilder.in(path);

        if (value instanceof Collection) {
            Collection list = (Collection) value;
            if (list.isEmpty()) {
                return new AbstractSimplePredicateExt(
                        (CriteriaBuilderImpl) criteriaBuilder, true, BooleanOperator.AND);
            } else {
                list.forEach(in::value);
            }
        } else {
            in.value(value);
        }

        return allowNull ? criteriaBuilder.or(in, criteriaBuilder.isNull(path)) : in;
    }

    /**
     * 是否允许为 {@code null}.
     *
     * @param annotation 注解实例
     * @return 布尔值
     */
    protected boolean isAllowNull(Object annotation) {
        try {
            return (boolean) this.getAnnotation().getMethod("allowNull").invoke(annotation);
        } catch (IllegalAccessException | IllegalArgumentException
                | InvocationTargetException | NoSuchMethodException | SecurityException e) {
            logger.error("【Jpa-plus 错误提示】获取【@In】、【@OrIn】、【@NotIn】、【@OrNotIn】相关注解中的【allowNull】的值失败,将默认返回 false 的值.", e);
            return false;
        }
    }

    /**
     * 构造模糊匹配条件 {@code LIKE} 的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildLikePredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        return criteriaBuilder.like(from.get(fieldName), "%" + this.convertValue(value) + "%");
    }

    /**
     * 构造模糊不匹配条件 {@code LIKE} 的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildNotLikePredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        return criteriaBuilder.notLike(from.get(fieldName), "%" + this.convertValue(value) + "%");
    }

    /**
     * 构造按前缀模糊匹配({@code LIKE})的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildStartsWithPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        return criteriaBuilder.like(from.get(fieldName), this.convertValue(value) + "%");
    }

    /**
     * 构造按前缀模糊不匹配({@code LIKE})的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildNotStartsWithPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        return criteriaBuilder.notLike(from.get(fieldName), this.convertValue(value) + "%");
    }

    /**
     * 构造按后缀模糊匹配({@code LIKE})的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildEndsWithPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        return criteriaBuilder.like(from.get(fieldName), "%" + this.convertValue(value));
    }

    /**
     * 构造按后缀模糊匹配({@code LIKE})的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildNotEndsWithPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        return criteriaBuilder.notLike(from.get(fieldName), "%" + this.convertValue(value));
    }

    /**
     * 构造按指定的模式做模糊匹配 {@code LIKE} 的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildLikePatternPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        return criteriaBuilder.like(from.get(fieldName), value.toString());
    }

    /**
     * 构造按指定的模式做模糊匹配 {@code LIKE} 的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 实体类的属性名
     * @param value 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildNotLikePatternPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        return criteriaBuilder.notLike(from.get(fieldName), value.toString());
    }

    /**
     * 构造多模糊条件 {@code LIKE OR LIKE} 的 {@link Predicate} 条件.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fields 实体类的属性名
     * @param values 对应属性的值
     * @param  泛型 Z
     * @param  泛型 X
     * @return {@link Predicate} 实例
     */
    protected  List buildLikeOrLikePredicates(
            CriteriaBuilder criteriaBuilder, From from, String[] fields, List values) {
        int len = fields.length;
        List predicates = new ArrayList<>(len);
        for (int i = 0; i < len; i++) {
            predicates.add(criteriaBuilder.like(from.get(fields[i]), "%" + this.convertValue(values.get(i)) + "%"));
        }
        return predicates;
    }

    private String convertValue(Object value) {
        return value.toString().replace("%", "\\%");
    }

    /**
     * 构造区间查询的 {@link Predicate} 实例的方法.
     * 若结束值为空,则退化生成为大于等于的条件,若开始值为空.则退化生成为小于等于的条件,若开始值或结束值都为空,则直接抛出异常.
     *
     * @param criteriaBuilder {@link CriteriaBuilder} 实例
     * @param from {@link From} 实例
     * @param fieldName 属性字段名称
     * @param value 值,只能是数组或者 {@link List} 集合类型.
     * @param  范型 Z
     * @param  范型 X
     * @return {@link Predicate} 实例
     */
    protected  Predicate buildBetweenPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object value) {
        if (value.getClass().isArray()) {
            Object[] arr = (Object[]) value;
            return this.buildBetweenPredicate(criteriaBuilder, from, fieldName, arr[0], arr[1]);
        } else if (value instanceof List) {
            List list = (List) value;
            return this.buildBetweenPredicate(criteriaBuilder, from, fieldName, list.get(0), list.get(1));
        } else if (value instanceof BetweenValue) {
            BetweenValue bv = (BetweenValue) value;
            return this.buildBetweenPredicate(criteriaBuilder, from, fieldName, bv.getStart(), bv.getEnd());
        } else {
            throw new BuildSpecificationException("【Jpa-plus 异常】构建【@Between】注解区间查询时,参数值类型不是数组或 "
                    + "List 类型的集合,无法获取到前后的区间值。");
        }
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private  Predicate buildBetweenPredicate(
            CriteriaBuilder criteriaBuilder, From from, String fieldName, Object startValue, Object endValue) {
        if (startValue != null && endValue != null) {
            this.isValueComparable(startValue);
            this.isValueComparable(endValue);
            return criteriaBuilder.between(from.get(fieldName), (Comparable) startValue, (Comparable) endValue);
        } else if (startValue != null) {
            this.isValueComparable(startValue);
            return criteriaBuilder.greaterThanOrEqualTo(from.get(fieldName), (Comparable) startValue);
        } else if (endValue != null) {
            this.isValueComparable(endValue);
            return criteriaBuilder.lessThanOrEqualTo(from.get(fieldName), (Comparable) endValue);
        } else {
            throw new BuildSpecificationException("【Jpa-plus 异常】构建【@Between】注解区间查询时,开始和结束的区间值均为【null】"
                    + ",无法构造区间或大于等于、小于等于的 Predicate条件。");
        }
    }

}