首先看一个查询的列子,输入一个名称,模糊匹配username或者nickName
return adminJpa.findAll(
adminJpa.support().like("username",name).or(support -> support.like("nickName",name)),
PageRequest.of(pageNum - 1,pageSize)
);
以上代码所打印的sql
Hibernate: select * from table table0_ where table0_.nick_name like ? or table0_.username like ? limit ?
相信你直接看都能看得懂以上代码所代表的是什么意思,那么这种操作是怎么实现的呢?
可以看到,我们只用了三四行的代码,就实现了原本需要十几行,并且可读性极差的代码所实现的功能
首先,我们先自定义一个BaseJpa,将原先继承JpaRepository的接口改成此接口,看以下代码,这里最主要的功能由JpaRepository
ps:修复了升级springboot 2.2.0错误
/**
* @author hexm
* @date 2019/8/20 10:54
*/
@NoRepositoryBean
public interface BaseJpa extends JpaRepository, JpaSpecificationExecutor {
/**
* 批量删除
* @param ids
* @return
*/
int deleteByIdIn(Collection ids);
/**
* 提供默认复杂查询支持
* @return
*/
default SpecificationSupport support(){
return SpecificationSupport.of();
}
}
我们需要做的就是提供一个Specification
ps:由于业务的升级,提供新的支持函数的查询方式
所以查询方式看起来有可能是这样子的?
下面还是工具类:
import org.hibernate.query.criteria.internal.CriteriaBuilderImpl;
import org.hibernate.query.criteria.internal.expression.function.BasicFunctionExpression;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigInteger;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.IntFunction;
/**
* Jpa复杂查询支持
*
* @author hexm
* @date 2019/8/19 15:25
*/
public class SpecificationSupport implements Specification {
/**
* 额外添加的or或者and条件
*/
private Map> AND_OR = new LinkedHashMap<>();
/**
* 里面都是and条件
*/
private List condition = new ArrayList<>();
/**
* 被忽略的属性
*/
private List ignoreColumn = new ArrayList<>();
/**
* 忽略大小写
*/
private List ignoreCase = new ArrayList<>();
/**构造时的对象*/
private T t;
/**
* 忽略空字符串
*/
private boolean ignoreNullString = true;
private SpecificationSupport() {
}
/**
* 得到一个查询构造器
*
* @param probe
* @param
* @param
* @return
*/
public static SpecificationSupport of(K probe) {
SpecificationSupport s = new SpecificationSupport<>();
s.t = probe;
return s;
}
/**
* 得到一个查询构造器
*
* @param
* @return
*/
public static SpecificationSupport of(Class clazz) {
return new SpecificationSupport<>();
}
/**
* 得到一个查询构造器
*
* @param
* @return
*/
public static SpecificationSupport of() {
return new SpecificationSupport<>();
}
/**
* 前匹配 like %column
*
* @param column
* @return
*/
public SpecificationSupport startsLike(String column) {
addCondition(column, OptionType.STARTS_LIKE);
return this;
}
/**
* 后匹配 like column%
*
* @param column
* @return
*/
public SpecificationSupport endLike(String column) {
addCondition(column, OptionType.END_LIKE);
return this;
}
/**
* 匹配 like %column%
*
* @param column
* @return
*/
public SpecificationSupport like(String column) {
addCondition(column, OptionType.LIKE);
return this;
}
/**
* 匹配 = column
*
* @param column
* @return
*/
public SpecificationSupport eq(String column) {
addCondition(column, OptionType.EQ);
return this;
}
/**
* 匹配 != column
*
* @param column
* @return
*/
public SpecificationSupport neq(String column) {
addCondition(column, OptionType.NOT_EQ);
return this;
}
/**
* 匹配 not like %column
*
* @param column
* @return
*/
public SpecificationSupport notStartLike(String column) {
addCondition(column, OptionType.NOT_STARTS_LIKE);
return this;
}
/**
* 匹配 not like column%
*
* @param column
* @return
*/
public SpecificationSupport notEndLike(String column) {
addCondition(column, OptionType.NOT_END_LIKE);
return this;
}
/**
* 匹配 not like %column%
*
* @param column
* @return
*/
public SpecificationSupport notLike(String column) {
addCondition(column, OptionType.NOT_LIKE);
return this;
}
/**
* 匹配 > column
*
* @param column 字段属性必须为数字类型,或字符串的数字
* @return
*/
public SpecificationSupport gt(String column) {
addCondition(column, OptionType.GT);
return this;
}
/**
* 匹配 >= column
*
* @param column 字段属性必须为数字类型,或字符串的数字
* @return
*/
public SpecificationSupport ge(String column) {
addCondition(column, OptionType.GE);
return this;
}
/**
* 匹配 < column
*
* @param column 字段属性必须为数字类型,或字符串的数字
* @return
*/
public SpecificationSupport lt(String column) {
addCondition(column, OptionType.LT);
return this;
}
/**
* 匹配 <= column
*
* @param column 字段属性必须为数字类型,或字符串的数字
* @return
*/
public SpecificationSupport le(String column) {
addCondition(column, OptionType.LE);
return this;
}
/**
* 前匹配 like %column
*
* @param column
* @return
*/
public SpecificationSupport startsLike(String column, String value) {
addCondition(column, OptionType.STARTS_LIKE, value);
return this;
}
/**
* 后匹配 like column%
*
* @param column
* @return
*/
public SpecificationSupport endLike(String column, String value) {
addCondition(column, OptionType.END_LIKE, value);
return this;
}
/**
* 匹配 like %column%
*
* @param column
* @return
*/
public SpecificationSupport like(String column, String value) {
addCondition(column, OptionType.LIKE, value);
return this;
}
/**
* 匹配 = column
*
* @param column
* @return
*/
public SpecificationSupport eq(String column, Object value) {
addCondition(column, OptionType.EQ, value);
return this;
}
/**
* 匹配 != column
*
* @param column
* @return
*/
public SpecificationSupport neq(String column, Object value) {
addCondition(column, OptionType.NOT_EQ, value);
return this;
}
/**
* 匹配 not like %column
*
* @param column
* @return
*/
public SpecificationSupport notStartLike(String column, String value) {
addCondition(column, OptionType.NOT_STARTS_LIKE, value);
return this;
}
/**
* 匹配 not like column%
*
* @param column
* @return
*/
public SpecificationSupport notEndLike(String column, String value) {
addCondition(column, OptionType.NOT_END_LIKE, value);
return this;
}
/**
* 匹配 not like %column%
*
* @param column
* @return
*/
public SpecificationSupport notLike(String column, String value) {
addCondition(column, OptionType.NOT_LIKE, value);
return this;
}
/**
* 匹配 > column
*
* @param column 字段属性必须为数字类型
* @return
*/
public SpecificationSupport gt(String column, Number value) {
addCondition(column, OptionType.GT, value);
return this;
}
/**
* 匹配 >= column
*
* @param column 字段属性必须为数字类型
* @return
*/
public SpecificationSupport ge(String column, Number value) {
addCondition(column, OptionType.GE, value);
return this;
}
/**
* 匹配 < column
*
* @param column 字段属性必须为数字类型
* @return
*/
public SpecificationSupport lt(String column, Number value) {
addCondition(column, OptionType.LT, value);
return this;
}
/**
* 匹配 <= column
*
* @param column 字段属性必须为数字类型
* @return
*/
public SpecificationSupport le(String column, Number value) {
addCondition(column, OptionType.LE, value);
return this;
}
/**
* 匹配 between column1 and column2
*
* @param column 字段
* @param value1 继承了比较接口的对象
* @param value2 继承了比较接口的对象
* @return
*/
public SpecificationSupport between(String column, Comparable value1, Comparable value2) {
addCondition(column, OptionType.BETWEEN, value1, value2);
return this;
}
/**
* 匹配 in (column)
*
* @param column 字段
* @param value 多个比较值
* @return
*/
public SpecificationSupport in(String column, Object... value) {
addCondition(column, OptionType.IN, value);
return this;
}
/**
* 匹配 column is null
*
* @param column 字段
* @return
*/
public SpecificationSupport isNull(String column) {
addConditionEmptyValue(column, OptionType.IS_NULL);
return this;
}
/**
* 匹配 column is not null
*
* @param column 字段
* @return
*/
public SpecificationSupport isNotNull(String column) {
addConditionEmptyValue(column, OptionType.IS_NOT_NULL);
return this;
}
/**
* 匹配 column == true
*
* @param column 字段
* @return
*/
public SpecificationSupport isTrue(String column) {
addConditionEmptyValue(column, OptionType.TRUE);
return this;
}
/**
* 匹配 column == false
*
* @param column 字段
* @return
*/
public SpecificationSupport isFalse(String column) {
addConditionEmptyValue(column, OptionType.FALSE);
return this;
}
/**
* 匹配 date_format(column,'format') ?= value
* 注:由于指定方法名为:date_format只适用于mysql,如果使用oracle需要改为to_char
* @param column 匹配字段
* @param value 匹配值
* @param format 匹配格式
* @return
*/
public SpecificationSupport dateFormat(String column, Object value, String format, OptionType optionType) {
Map argTypeMap = new HashMap<>();
argTypeMap.put(column, ArgType.ROOT); //第一个参数读取字段
argTypeMap.put(format, ArgType.STRING); //第二个参数字符串类型
function("date_format", new String[]{column, format}, argTypeMap, optionType, false, value);
return this;
}
/**
* 匹配 date_format(column,'format') = value
*
* @param column 匹配字段
* @param value 匹配值
* @param format 匹配格式
* @return
*/
public SpecificationSupport dateEq(String column, Object value, String format) {
dateFormat(column, value, format, OptionType.EQ);
return this;
}
/**
* 匹配 date_format(column,'format') >= value
*
* @param column 匹配字段
* @param value 匹配值
* @param format 匹配格式
* @return
*/
public SpecificationSupport dateGe(String column, Object value, String format) {
dateFormat(column, value, format, OptionType.GE);
return this;
}
/**
* 匹配 date_format(column,'format') <= value
*
* @param column 匹配字段
* @param value 匹配值
* @param format 匹配格式
* @return
*/
public SpecificationSupport dateLe(String column, Object value, String format) {
dateFormat(column, value, format, OptionType.LE);
return this;
}
/**
* 匹配 function(column) ? value
*
* @param functionName 方法名称
* @param args 参数名
* @param typeMap 参数类型
* @param optionType 比较类型
* @param emptyValue 是否没有参数
* @param value 参数
* @return
*/
public SpecificationSupport function(String functionName, Object[] args, Map typeMap, OptionType optionType, boolean emptyValue, Object... value) {
Class> clazz;
//不是空参数,根据比较的参数类型,指定函数的返回类型
if (!emptyValue && value != null && value[0] != null) {
clazz = value[0].getClass();
} else {
clazz = Boolean.class;
}
SupportFunction supportFunction = new SupportFunction(functionName, args, typeMap, optionType, clazz);
addConditions(null, OptionType.FUNCTION, emptyValue, supportFunction, value);
return this;
}
/**
* 忽略某个字段属性
*
* @param columns
* @return
*/
public SpecificationSupport ignoreProperty(String... columns) {
ignoreColumn.addAll(Arrays.asList(columns));
return this;
}
/**
* 忽略大小写匹配 = lower(column)
*
* @param columns
* @return
*/
public SpecificationSupport ignoreCase(String... columns) {
ignoreCase.addAll(Arrays.asList(columns));
return this;
}
/**
* 添加条件
*
* @param column
* @param optionType
*/
private void addCondition(String column, OptionType optionType) {
try {
addCondition(column, optionType, ObjectUtils.getField(t, column).get(t));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* 添加条件
*
* @param column
* @param optionType
*/
private void addConditionEmptyValue(String column, OptionType optionType) {
addConditions(column, optionType, true, null);
}
/**
* 添加条件
*
* @param column
* @param optionType
*/
@SafeVarargs
private final void addCondition(String column, OptionType optionType, K... value) {
addConditions(column, optionType, false, null, value);
}
/**
* 添加条件
*
* @param column
* @param optionType
*/
@SafeVarargs
private final void addConditions(String column, OptionType optionType, boolean emptyValue, SupportFunction function, K... value) {
if (emptyValue) {
condition.add(new FieldOption<>(column, optionType, value, function));
} else {
boolean flag = true;
for (K k : value) {
if (k == null) {
flag = false;
break;
}
if ("".equals(k) && ignoreNullString) {
flag = false;
break;
}
}
if (flag) {
condition.add(new FieldOption<>(column, optionType, value, function));
}
}
}
/**
* and操作,创建一个消费者,依靠lambed表达式,消费一个创建好的对象,以添加到定义的队列中
*
* @param and
* @return
*/
public SpecificationSupport and(Consumer> and) {
if (and != null) {
SpecificationSupport s = SpecificationSupport.of();
and.accept(s);
if (!s.isEmpty()) {
AND_OR.put(OptionType.AND, s);
}
}
return this;
}
/**
* or操作,创建一个消费者,依靠lambed表达式,消费一个创建好的对象,以添加到定义的队列中
*
* @param or
* @return
*/
public SpecificationSupport or(Consumer> or) {
if (or != null) {
SpecificationSupport s = SpecificationSupport.of();
or.accept(s);
if (!s.isEmpty()) {
AND_OR.put(OptionType.OR, s);
}
}
return this;
}
/**
* @param root
* @param query
* @param cb
* @return
*/
@Override
public Predicate toPredicate(Root root, CriteriaQuery> query, CriteriaBuilder cb) {
if (t != null) {
Map fieldMap = ObjectUtils.getFieldMap(t.getClass());
fieldMap.forEach((s1, field) -> {
try {
if (!Modifier.isFinal(field.getModifiers())) {
if (!condition.contains(new FieldOption<>(field.getName(), null, null))) {
addCondition(field.getName(), OptionType.EQ, field.get(t));
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
}
Predicate p = null;
List predicates = new ArrayList<>();
for (FieldOption option : condition) {
//不在忽略字段中
if (!ignoreColumn.contains(option.name)) {
Predicate predicate = getPredicate(root, cb, option);
if (predicate != null) {
predicates.add(predicate);
}
}
}
if (!predicates.isEmpty()) {
p = cb.and(predicates.toArray(new Predicate[0]));
}
for (Map.Entry> entry : AND_OR.entrySet()) {
OptionType optionType = entry.getKey();
SpecificationSupport specificationSupport = entry.getValue();
if (optionType == OptionType.AND) {
if (p == null) {
p = cb.and(specificationSupport.toPredicate(root, query, cb));
} else {
p = cb.and(specificationSupport.toPredicate(root, query, cb), p);
}
} else {
if (p == null) {
p = cb.or(specificationSupport.toPredicate(root, query, cb));
} else {
p = cb.or(specificationSupport.toPredicate(root, query, cb), p);
}
}
}
return p;
}
/**
* 设置是否忽略空串
*
* @param ignoreNullString
*/
public void setIgnoreNullString(boolean ignoreNullString) {
this.ignoreNullString = ignoreNullString;
}
/**
* 判断构造器条件是否为空
*
* @return
*/
protected boolean isEmpty() {
return condition.isEmpty() && AND_OR.isEmpty();
}
/**
* 判断条件类型,添加到组合中
*
* @param root
* @param cb
* @param option
* @return
*/
@SuppressWarnings("unchecked")
private Predicate getPredicate(Root root, CriteriaBuilder cb, FieldOption option) {
switch (option.getOptionType()) {
case EQ:
if (ignoreCase.contains(option.getName())) {
return cb.equal(cb.lower(root.get(option.getName())), option.getValue()[0].toString().toLowerCase());
}
return cb.equal(root.get(option.getName()), option.getValue()[0]);
case GE:
return cb.ge(root.get(option.getName()), (Number) option.getValue()[0]);
case GT:
return cb.gt(root.get(option.getName()), (Number) option.getValue()[0]);
case IN:
if (ignoreCase.contains(option.getName())) {
CriteriaBuilder.In
关于以上代码中用到的其他工具代码:
/**
* 获取对象Field属性对象集合ap
*
* @param clazz
* @return
*/
public static Map getFieldMap(Class clazz) {
Map result = new LinkedHashMap(16);
for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
result.put(field.getName(), field);
}
}
return result;
}
//ps:之前漏了一些方法的说明,补充一下,关于ObjectUtils中涉及到的方法
//另外补充一下,其实将Field保存为Map,使用key的方式搜索会更好一点
/**
* 获取对象Field属性对象
*
* @param clazz
* @param name 属性名称
* @return
*/
public static Field getField(Class clazz, String name) {
for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (name.equals(field.getName())) {
field.setAccessible(true);
return field;
}
}
}
return null;
}
/**
* 获取对象Field属性对象
*
* @param obj
* @param name
* @param
* @return
*/
public static Field getField(T obj, String name) {
return getField(obj.getClass(), name);
}
这些你都可以复制后直接使用!
具体代码实现原理这里不再累赘,网上很多这方面的资料。
由于时间问题只看了两天的资料,所以有一些疏漏的地方轻喷,请指正。