1.利用JPA的Specification接口和元模型就实现动态查询了。但是这样每一个需要动态查询的地方都需要写一个这样类似的findByConditions方法,小型项目还好,大型项目中其实会造成人力资源的浪费,进行了大量的重复工作,所以想着对动态查询进行封装,使其使用起来更加方便。
在开发中,用到动态查询的地方,所有的查询条件包括分页参数,都会被封装成一个查询类XxxQuery,我们封装的思路是创建一个BaseQuery类,在其中实现动态查询的封装,即提供几个模板方法,将查询类的所有属性按照连接规则,拼装成一个Specification型的对象返回,那么问题来了,如何去标识这些字段该用怎样的查询条件连接呢,还要考虑到每个查询类都可以通用,可以用字段注解,来标识字段的查询连接条件。
创建枚举类MatchType,列出所有的连接条件
package powerx.io;
public enum MatchType {
equal, // filed = value
//下面四个用于Number类型的比较
gt, // filed > value
ge, // field >= value
lt, // field < value
le, // field <= value
notEqual, // field != value
like, // field like value
notLike, // field not like value
// 下面四个用于可比较类型(Comparable)的比较
greaterThan, // field > value
greaterThanOrEqualTo, // field >= value
lessThan, // field < value
lessThanOrEqualTo // field <= value
}
2.自定义注解,用来标识字段
package powerx.io;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface QueryCondition {
// 数据库中字段名,默认为空字符串,则Query类中的字段要与数据库中字段一致
String column() default "";
// equal, like, gt, lt...
MatchType func() default MatchType.equal;
// object是否可以为null
boolean nullable() default false;
// 字符串是否可为空
boolean emptyable() default false;
}
3.BaseQuery
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import java.lang.reflect.Field;
import java.util.*;
public abstract class BaseQuery {
private int pageNum = 1;
private int pageSize = 10;
public Pageable toPageable() {
return PageRequest.of(pageNum - 1, pageSize);
}
public Pageable toPageable(Sort sort) {
return PageRequest.of(pageNum - 1, pageSize, sort);
}
public Query findPage() {
return find().with(toPageable());
}
public Query findPage(Pageable pageable) {
return find().with(pageable);
}
public Query findSort(Sort sort) {
return find().with(sort);
}
public Query findPageSort(Sort sort) {
return findPage(toPageable(sort));
}
public Query findPageSort(Pageable pageable, Sort sort) {
return findPage(pageable).with(sort);
}
public Query find() {
Class clazz = this.getClass();
List fields = getAllFieldsWithRoot(clazz);
Criteria criteria = new Criteria();
Set setKey = new HashSet<>();
for (Field field : fields) {
QueryCondition qw = field.getAnnotation(QueryCondition.class);
if (qw == null)
continue;
String column = qw.column();
if (column.equals(""))
column = field.getName();
field.setAccessible(true);
try {
Object value = field.get(this);
if (value == null && !qw.nullable())
continue;
if (value != null && String.class.isAssignableFrom(value.getClass())) {
String s = (String) value;
if ("".equals(s) && !qw.emptyable())
continue;
}
//判断是否重复
if (!setKey.contains(column)) {
criteria = criteria.and(column);
setKey.add(column);
}
switch (qw.func()) {
case eq:
criteria.is(value);
break;
case gt:
criteria.gt(value);
break;
case lt:
criteria.lt(value);
break;
case ge:
criteria.gte(value);
break;
case le:
criteria.lte(value);
break;
case ne:
criteria.ne(value);
break;
case like:
criteria.regex("^" + value + "^");
break;
case regex:
criteria.regex(qw.regex(), String.valueOf(value));
break;
}
} catch (Exception e) {
continue;
}
}
return Query.query(criteria);
}
private List getAllFieldsWithRoot(Class> clazz) {
List fieldList = new ArrayList<>();
Field[] dFields = clazz.getDeclaredFields();
if (null != dFields && dFields.length > 0)
fieldList.addAll(Arrays.asList(dFields));
Class> superClass = clazz.getSuperclass();
if (superClass == Object.class) return Arrays.asList(dFields);
List superFields = getAllFieldsWithRoot(superClass);
if (null != superFields && !superFields.isEmpty()) {
superFields.stream().
filter(field -> !fieldList.contains(field)).forEach(field -> fieldList.add(field));
}
return fieldList;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
}
4.实体类(使用@QueryCondition注解)
public class UserCoreQueryRequest extends BaseQuery{
@ApiModelProperty("用户id")
@QueryCondition
private Integer user_id;