定义比较类型
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);