环境
JDK11
SpringBoot 2.7.11
querydsl 5.0.0
mysql-connector-j 8.0.33
前言
使用获MybatisPlus的同学,当遇到SpringJPA的项目,那真的崩溃。构建一个条件,需要判断空,不判断就报错了,一行代码的事情,必须写三行。用过的都懂
解决办法
使用反射和dsl封装一下。只要写一个入参的类,就可以构造出查询条件直接查询,免去了繁琐的if判断
测试类如下
构造查询参数BookQuery ,使用封装的QObject 构造查询参数直接查询
@SpringBootTest
public class BookRepositoryTest extends TestCommon {
@Resource
BookRepository bookRepository;
@Resource
JPAQueryFactory queryFactory;
@Test
void repo() {
// 构造查询参数
BookQuery bookQuery = new BookQuery();
bookQuery.setRange(Range.between(15, 20));
// 查询
QObject qry = new QObject(BookJpa.class, bookQuery);
Predicate exp = qry.predicate();
System.out.println(exp);
System.out.println(bookRepository.findAll(exp));
}
@Test
void fact() throws ParseException {
// 构造查询参数
BookQuery bookQuery = new BookQuery();
bookQuery.setRange(Range.between(15, 20));
bookQuery.setDayRange(Range.between(DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2024-12-30"), DateFormatUtils.ISO_8601_EXTENDED_DATE_FORMAT.parse("2024-12-31")));
// 查询
QObject qry = new QObject(BookJpa.class, bookQuery);
Predicate exp = qry.predicate();
System.out.println(exp);
List<?> list = queryFactory.from(qry).where(exp).offset(1).limit(3).fetch();
System.out.println(list);
}
}
关键代码
- QObject
public class QObject extends EntityPathBase<Object> {
private Object queryParam;
public QObject(Class<?> clazz, Object queryParam) {
super(clazz, variable(clazz));
this.queryParam = queryParam;
}
static String variable(Class<?> clazz) {
char[] charArray = clazz.getSimpleName().toCharArray();
charArray[0] = Character.toLowerCase(charArray[0]);
return String.valueOf(charArray);
}
@Override
public BooleanPath createBoolean(String property) {
return super.createBoolean(property);
}
@Override
public <A extends Enum<A>> EnumPath<A> createEnum(String property, Class<A> type) {
return super.createEnum(property, type);
}
@Override
public <A extends Comparable> DatePath<A> createDate(String property, Class<? super A> type) {
return super.createDate(property, type);
}
@Override
public <A extends Comparable> DateTimePath<A> createDateTime(String property, Class<? super A> type) {
return super.createDateTime(property, type);
}
@Override
public <A extends Number & Comparable<?>> NumberPath<A> createNumber(String property, Class<? super A> type) {
return super.createNumber(property, type);
}
@Override
public StringPath createString(String property) {
return super.createString(property);
}
public <A extends Comparable> ComparablePath<A> createComparable(String property, Class<? super A> type) {
return super.createComparable(property, type);
}
@Override
public <A extends Comparable> TimePath<A> createTime(String property, Class<? super A> type) {
return super.createTime(property, type);
}
public <T> Predicate predicate() {
return QueryUtils.predicate(queryParam, this);
}
}
- QueryUtils
public class QueryUtils {
public static <T> Predicate predicate(Object param, Class<T> clazz) {
QObject q = new QObject(clazz, param);
return predicate(param, q);
}
public static Predicate predicate(Object param, QObject q) {
Predicate qr = null;
Field[] fields = FieldUtils.getAllFields(param.getClass());
try {
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
Class<Comparable<?>> type = (Class<Comparable<?>>) field.getType();
Object value = field.get(param);
if (ObjectUtil.isNotEmpty(value)) {
if (value instanceof Range) {
Range<? extends Comparable> range = (Range) value;
type = (Class<Comparable<?>>) range.getMaximum().getClass();
qr = ExpressionUtils.and(qr, q.createComparable(name, type).between(range.getMinimum(), range.getMaximum()));
} else if (value instanceof Comparable) {
qr = ExpressionUtils.and(qr, q.createComparable(name, type).in((Comparable) value));
} else if (value instanceof Collection) {
Collection<? extends Comparable<?>> collection = (Collection<? extends Comparable<?>>) value;
type = (Class<Comparable<?>>) collection.iterator().next().getClass();
qr = ExpressionUtils.and(qr, q.createComparable(name, type).in(collection));
} else if (value.getClass().isArray()) {
Collection<? extends Comparable<?>> collection = Arrays.stream(((Object[]) value)).map(item -> (Comparable<?>) item).collect(Collectors.toList());
type = (Class<Comparable<?>>) collection.iterator().next().getClass();
qr = ExpressionUtils.and(qr, q.createComparable(name, type).in(collection));
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return qr;
}
}
- BookQuery
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* @author dzh
* @since 2024/12/30
*/
@Data
public class BookQuery {
private List<Long> id;
private List<String> title;
private Range<Integer> range;
private Range<Date> dayRange;
}
- BookJpa
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
* @author dzh
* @since 2024/12/30
*/
@Data
@Entity
@Table(name = "book")
public class BookJpa {
@Id
private Long id;
private String title;
private Integer range;
private Date dayRange;
}
- BookRepository
import com.db.entity.BookJpa;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface BookRepository extends JpaRepository<BookJpa,Integer>, QuerydslPredicateExecutor<BookJpa> {
}
- Range
package com.db.query;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 区间
* @author dzh
* @since 2024/12/31
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Range<T> {
private T minimum;
private T maximum;
}
总结
毕竟是封装的功能,肯定没有原始的灵活,但是这个已经解决了项目中95%以上的查询逻辑。
有人说反射耗费性能,可以自行增加缓存,或者调用有缓存的工具类