querydsl查询工具类

23 阅读4分钟

定义比较类型

public enum JudgeType {
    INCLUDE, ANY_INCLUDE,EQ, IN, NOT_IN, LIKE, LE,LT,GT, GE,BETWEEN, LIKE_AND_IN, ANY_IN;
}

注解类

//不加默认为eq,BETWEEN为日期过滤的标准 需要用Pair<LocalDateTime,LocalDateTime>
//IN为集合List null为没有该条件 【】返回查询filter为null

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ConditionPredicate {
    JudgeType judgeType() default JudgeType.EQ;
}

根据注解来拼接条件

package com.example.spring3.querydsl;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.data.domain.Pageable;

import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.BeanPath;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.CollectionPathBase;
import com.querydsl.core.types.dsl.ComparableExpression;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.core.types.dsl.SimpleExpression;
import com.querydsl.core.types.dsl.StringExpression;

import lombok.SneakyThrows;

public interface EntityQuery<T extends BeanPath> extends Serializable {




    /**
     * 对自动生成的条件进行后置处理 来完成条件的构建  默认什么都不做
     * 一般是用关联查询
     *
     * @return
     */
    default Predicate postProcess(BooleanExpression predicate) {
        return predicate;
    }

    @SneakyThrows
    default Predicate toQuery() {
        return toQuery(null, null, null);
    }

    /**
     * 内部递归调用
     *
     * @param sql       上一步已经拼接好的sql
     * @param qPath     需要关联查询的QEntity的对象 QA{QB qB;}中的b
     * @param condition 关联查询的条件 queryEntity对象
     * @return
     */
    @SneakyThrows
    private Predicate toQuery(BooleanExpression sql, Expression qPath, EntityQuery condition) {
        if (condition == null) condition = this;
        final Class<? extends Expression> qClass = qPath == null ? getQEntityClass(this.getClass()) : qPath.getClass();//获得QEntity的CLass对象
        final Map<String, Field> qFieldMap = Arrays.stream(qClass.getFields()).collect(Collectors.toMap(Field::getName, Function.identity()));//获得QEntity所有属性
        if (qPath == null) qPath = getQClassExample(qClass);//解决内部调用 外部调用一定为null,获取获得QEntity.qEntity对象
//        if (sql == null) sql = ((BeanPath) qPath).isNotNull();//解决内部调用外部调用一定为null 设置sql的默认条件即查询的记录不为null 已经用and优化掉
        Field[] fields = condition.getClass().getDeclaredFields();//拿到所有条件的字段
        for (Field field : fields) {//遍历 条件x
            field.setAccessible(true);
            Object fieldValue;//获取条件x的值
            final ConditionPredicate fieldAnnotation = field.getAnnotation(ConditionPredicate.class);
            if (fieldAnnotation != null && (fieldValue = field.get(condition)) != null) { //为null为不加限制,没有注解也为不加限制 可以解决循环的问题因为AQuery{BQuery{AQuery:aQuery}:bQuery} 里层的qQuery一定为null此时会跳过 如果你不想出意外的话
                //获取该条件x在QEntity中对于的字段 这里不用判断get()==null 是因为是开发问题 你应该一开始就写对而不是兜底
                final Expression expression = (Expression) qFieldMap.get(field.getName().split("_")[0]).get(qPath);

                switch (fieldAnnotation.judgeType()) {
                    case INCLUDE:
                        sql = and(sql,((EntityQuery) fieldValue).toQuery(null, expression, (EntityQuery) fieldValue));
//                        sql =(BooleanExpression) ((EntityQuery) fieldValue).toQuery(sql, expression, (EntityQuery) fieldValue);
                        break;
                    case ANY_INCLUDE:
                        sql = and(sql,((EntityQuery) fieldValue).toQuery(sql, ((CollectionPathBase) expression).any(), (EntityQuery) fieldValue));
                        break;
                    case EQ:
                        sql = and(sql,((SimpleExpression) expression).eq(fieldValue));
                        break;
                    case IN:
                        sql = and(sql,((SimpleExpression) expression).in((Collection) fieldValue));
                        break;
                    case ANY_IN:
                        sql = and(sql,((CollectionPathBase) expression).any().in((Collection) fieldValue));
                        break;
                    case LIKE_AND_IN:
                        final List<?> o1 = (List<?>) fieldValue;
                        if (o1.size() == 1) {//如果集合大小等于1 可能是like
                            if (Objects.equals(null, o1.get(0)))
                                sql = and(sql,((SimpleExpression) expression).isNull());
                            else if (o1.get(0) instanceof String)
                                sql = and(sql,((StringExpression) expression).like("%" + o1.get(0) + "%"));
                            else
                                sql = and(sql,((SimpleExpression) expression).in((Collection) fieldValue));
                        } else
                            sql = and(sql,((SimpleExpression) expression).in((Collection) fieldValue));
                        break;
                    case LIKE:
                        sql = and(sql,((StringExpression) expression).like("%" + fieldValue + "%"));
                        break;
                    case LT:
                        sql = and(sql,((ComparableExpression) expression).lt((Comparable) fieldValue));
                        break;
                    case GT:
                        sql = and(sql,
                                expression instanceof ComparableExpression?
                                        ((ComparableExpression) expression).gt((Comparable) fieldValue)
                                        :((NumberExpression) expression).gt((Number)fieldValue));
                        break;
                    case GE:
                        sql = and(sql,((ComparableExpression) expression).goe((Comparable) fieldValue));
                        break;
                    case LE:
                        sql = and(sql,((ComparableExpression) expression).loe((Comparable) fieldValue));
                        break;
                    case BETWEEN:
                        final Object start = ((ComparablePair) fieldValue).getFirst();
                        final Object end = ((ComparablePair) fieldValue).getSecond();
                        if (Objects.nonNull(start)) {
                            if (expression instanceof ComparableExpression)
                                sql = and(sql,((ComparableExpression) expression).goe((Comparable) start));
                            else
                                sql = and(sql,((NumberExpression) expression).goe((Number) start));
                        }
                        if (Objects.nonNull(end)) {
                            if (expression instanceof ComparableExpression)
                                sql = and(sql,((ComparableExpression) expression).loe((Comparable) end));
                            else
                                sql = and(sql,((NumberExpression) expression).loe((Number) end));
                        }
                        break;
                    default:
                        throw new RuntimeException("暂不支持 请在QueryBuilder完成对应的逻辑");

                }
            }
        }
        return postProcess(sql);


    }
    private BooleanExpression and (BooleanExpression sql,Predicate right){
        if (sql == null) return (BooleanExpression) right;
        else return sql.and(right);
        
    }

    private Class<? extends Expression> getQEntityClass(Class<? extends EntityQuery> queryClass) {
        final ParameterizedType genericSuperclass = (ParameterizedType)queryClass.getGenericSuperclass();
        final Class<? extends Expression> qClass =
                (Class<? extends Expression>)(genericSuperclass.getActualTypeArguments()[0]);
        //不要抽象类直接implements QueryBuilder<QEmployeeRole>
//        final Type type =
//                Arrays.stream(queryClass.getGenericInterfaces())
//                        .filter(x -> x instanceof ParameterizedType && ("com.example.spring3.querydsl").equals(((ParameterizedType) x).getRawType().getTypeName())).findAny().orElseThrow(() -> new RuntimeException(queryClass.getName()+"未找到实现QueryBuilder的接口"));
//        final Class<? extends Expression> qClass = (Class<? extends Expression>) ((ParameterizedType) type).getActualTypeArguments()[0];
        return qClass;
    }

    private static <Q extends Expression> Q getQClassExample(Class<? extends Expression> qClass) throws
            NoSuchFieldException, IllegalAccessException {
        return (Q) qClass.getField(firstLowerCase(qClass.getSimpleName().substring(1))).get(qClass);

    }

    public static String firstCapital(String name) {
        char[] cs = name.toCharArray();
        cs[0] -= 32;
        return String.valueOf(cs);

    }

    public static String firstLowerCase(String name) {
        char[] cs = name.toCharArray();
        cs[0] += 32;
        return String.valueOf(cs);

    }

}

查询分页包装类

public abstract class AbstractQuery<T extends BeanPath> implements QueryBuilder<T>{

    Pageable Pageable;

    public Pageable getPageable() {
        return Pageable;
    }

    public void setPageable(Pageable Pageable) {
        Pageable = Pageable;
    }
}
public interface QueryBuilder<T extends BeanPath> extends EntityQuery<T>,Serializable {
    public Pageable getPageable();

}

比较条件类 其他的比如

@EqualsAndHashCode(callSuper = true)
@Data@NoArgsConstructor
public  class ComparablePair<F extends Comparable,S extends Comparable> extends Pair<F,S> {
    public static <F extends Comparable,S extends Comparable>  ComparablePair<F,S> of(F first, S second) {
        return new ComparablePair<F,S>(first, second);
    }
    public ComparablePair(F first, S second) {
        super(first, second);
    }
}
@EqualsAndHashCode(callSuper = true)
@Data@NoArgsConstructor
public class LocalDatePair extends ComparablePair<LocalDate,LocalDate>{
    @JsonFormat(pattern = "yyyy-MM-dd")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate first;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate second;

    public static  LocalDatePair of(LocalDate first, LocalDate second) {
        return new LocalDatePair(first, second);
    }

    public LocalDatePair(LocalDate first, LocalDate second) {
        super(first, second);
        this.first=first;
        this.second=second;
    }
}
@EqualsAndHashCode(callSuper = true)
@Data@NoArgsConstructor
public class LocalDateTimePair extends ComparablePair<LocalDateTime,LocalDateTime>    {
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private  LocalDateTime first;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private  LocalDateTime second;

    public static  LocalDateTimePair of(LocalDateTime first, LocalDateTime second) {
        return new LocalDateTimePair(first, second);
    }
    public LocalDateTimePair(LocalDateTime first, LocalDateTime second) {
        super(first, second);
        this.first=first;
        this.second=second;
    }
}

查询 本身还是模板 if(name!=null) sql.and(name.eq(name)) if....

// 查询某个用户下所以房产
final List<House> byUserId = houseRepository.findByOwner_Id(46L);
// 当你有一个自定义查询
final QUser user = QUser.user;
userRepository.findAll(user.age.eq(25));
// 现在要新增条件或者新写一个查询
userRepository.findAll(user.age.eq(25).and(user.createTime.between(LocalDateTime.of(2024, Month.APRIL,3,5,5),
        LocalDateTime.of(2026, Month.APRIL,3,5,5))));
// 注意思路是写一个大而范用的查询方法,根据各个业务逻辑去设置相应的字段内容,根据反射来生成sql
// 本身还是模板  if(name!=null) sql.and(name.eq(name)) if....
// 而现在 你只需要去在查询一开始就创建好查询类
final UserQuery query = new UserQuery();
query.setAge(25);
// 对于不同的查询去set不同的值就行
query.setCreateTime(LocalDateTimePair.of(LocalDateTime.of(2024, Month.APRIL,3,5,5),
        LocalDateTime.of(2026, Month.APRIL,3,5,5)));
final CityQuery city = new CityQuery();
query.setCity(city);
city.setName_Like("1");
final Iterable<User> all = userRepository.findAll(query.toQuery());

// 分页也可以直接userRepository.findAll(query.toQuery(),query.getPageable())
query.setPageable(PageRequest.of(1,5));
final Page<User> page = PageUtils.getPage(userRepository, query);
System.out.println(1);