如何优雅的使用Jpa的JpaSpecificationExecutor提供的复杂查询,实现Specification对象的简单操作
2019-08-23 00:06
1401 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/a627428179/article/details/100026568
首先看一个查询的列子,输入一个名称,模糊匹配username或者nickName
[code]return adminJpa.findAll( adminJpa.support().like("username",name).or(support -> support.like("nickName",name)), PageRequest.of(pageNum - 1,pageSize) );
以上代码所打印的sql
[code]Hibernate: select * from table table0_ where table0_.nick_name like ? or table0_.username like ? limit ?
相信你直接看都能看得懂以上代码所代表的是什么意思,那么这种操作是怎么实现的呢?
可以看到,我们只用了三四行的代码,就实现了原本需要十几行,并且可读性极差的代码所实现的功能
代码块
首先,我们先自定义一个BaseJpa,将原先继承JpaRepository的接口改成此接口,看以下代码,这里最主要的功能由JpaRepository<T>提供,复杂查询由JpaSpecificationExecutor<T>提供
[code]/** * @author hexm * @date 2019/8/20 10:54 */ @NoRepositoryBean public interface BaseJpa<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> { /** * 批量删除 * @param ids * @return */ int deleteByIdIn(Iterable<ID> ids); /** * 提供默认复杂查询支持 * @return */ default SpecificationSupport<T> support(){ return SpecificationSupport.of(); } }
我们需要做的就是提供一个Specification<T>的实现,这个实现是JpaSpecificationExecutor接口所要求的参数对象,对此我们做了一层简单的封装,以达到平常需要用到的条件查询,如果你找不到你要的方法,可以在原有的基础上进行添加,相信这一定很简单!下面提供Specification实现
[code]/** * Jpa复杂查询支持 * * @author hexm * @date 2019/8/19 15:25 */ public class SpecificationSupport<T> implements Specification<T> { /** * 额外添加的or或者and条件 */ private Map<OptionType, SpecificationSupport<T>> AND_OR = new LinkedHashMap<>(); /** * 里面都是and条件 */ private List<FieldOption> condition = new ArrayList<>(); /** * 被忽略的属性 */ private List<String> ignoreColumn = new ArrayList<>(); /** * 忽略大小写 */ private List<String> ignoreCase = new ArrayList<>(); private T t; private boolean ignoreNullString = true; private SpecificationSupport() { } /** * 得到一个查询构造器 * * @param probe * @param <T> * @param <K> * @return */ public static <T, K extends T> SpecificationSupport<T> of(K probe) { SpecificationSupport<T> s = new SpecificationSupport<>(); s.t = probe; return s; } /** * 得到一个查询构造器 * * @param <T> * @return */ public static <T> SpecificationSupport<T> of(Class<T> clazz) { return new SpecificationSupport<>(); } /** * 得到一个查询构造器 * * @param <T> * @return */ public static <T> SpecificationSupport<T> of() { return new SpecificationSupport<>(); } /** * 前匹配 like %column * * @param column * @return */ public SpecificationSupport<T> startsLike(String column) { addCondition(column, OptionType.STARTS_LIKE); return this; } /** * 后匹配 like column% * * @param column * @return */ public SpecificationSupport<T> endLike(String column) { addCondition(column, OptionType.END_LIKE); return this; } /** * 匹配 like %column% * * @param column * @return */ public SpecificationSupport<T> like(String column) { addCondition(column, OptionType.LIKE); return this; } /** * 匹配 = column * * @param column * @return */ public SpecificationSupport<T> eq(String column) { addCondition(column, OptionType.EQ); return this; } /** * 匹配 != column * * @param column * @return */ public SpecificationSupport<T> neq(String column) { addCondition(column, OptionType.NOT_EQ); return this; } /** * 匹配 not like %column * * @param column * @return */ public SpecificationSupport<T> notStartLike(String column) { addCondition(column, OptionType.NOT_STARTS_LIKE); return this; } /** * 匹配 not like column% * * @param column * @return */ public SpecificationSupport<T> notEndLike(String column) { addCondition(column, OptionType.NOT_END_LIKE); return this; } /** * 匹配 not like %column% * * @param column * @return */ public SpecificationSupport<T> notLike(String column) { addCondition(column, OptionType.NOT_LIKE); return this; } /** * 匹配 > column * * @param column 字段属性必须为数字类型,或字符串的数字 * @return */ public SpecificationSupport<T> gt(String column) { addCondition(column, OptionType.GT); return this; } /** * 匹配 >= column * * @param column 字段属性必须为数字类型,或字符串的数字 * @return */ public SpecificationSupport<T> ge(String column) { addCondition(column, OptionType.GE); return this; } /** * 匹配 < column * * @param column 字段属性必须为数字类型,或字符串的数字 * @return */ public SpecificationSupport<T> lt(String column) { addCondition(column, OptionType.LT); return this; } /** * 匹配 <= column * * @param column 字段属性必须为数字类型,或字符串的数字 * @return */ public SpecificationSupport<T> le(String column) { addCondition(column, OptionType.LE); return this; } /** * 前匹配 like %column * * @param column * @return */ public SpecificationSupport<T> startsLike(String column, String value) { addCondition(column, OptionType.STARTS_LIKE, value); return this; } /** * 后匹配 like column% * * @param column * @return */ public SpecificationSupport<T> endLike(String column, String value) { addCondition(column, OptionType.END_LIKE, value); return this; } /** * 匹配 like %column% * * @param column * @return */ public SpecificationSupport<T> like(String column, String value) { addCondition(column, OptionType.LIKE, value); return this; } /** * 匹配 = column * * @param column * @return */ public SpecificationSupport<T> eq(String column, Object value) { addCondition(column, OptionType.EQ, value); return this; } /** * 匹配 != column * * @param column * @return */ public SpecificationSupport<T> neq(String column, Object value) { addCondition(column, OptionType.NOT_EQ, value); return this; } /** * 匹配 not like %column * * @param column * @return */ public SpecificationSupport<T> notStartLike(String column, String value) { addCondition(column, OptionType.NOT_STARTS_LIKE, value); return this; } /** * 匹配 not like column% * * @param column * @return */ public SpecificationSupport<T> notEndLike(String column, String value) { addCondition(column, OptionType.NOT_END_LIKE, value); return this; } /** * 匹配 not like %column% * * @param column * @return */ public SpecificationSupport<T> notLike(String column, String value) { addCondition(column, OptionType.NOT_LIKE, value); return this; } /** * 匹配 > column * * @param column 字段属性必须为数字类型 * @return */ public SpecificationSupport<T> gt(String column, Number value) { addCondition(column, OptionType.GT, value); return this; } /** * 匹配 >= column * * @param column 字段属性必须为数字类型 * @return */ public SpecificationSupport<T> ge(String column, Number value) { addCondition(column, OptionType.GE, value); return this; } /** * 匹配 < column * * @param column 字段属性必须为数字类型 * @return */ public SpecificationSupport<T> lt(String column, Number value) { addCondition(column, OptionType.LT, value); return this; } /** * 匹配 <= column * * @param column 字段属性必须为数字类型 * @return */ public SpecificationSupport<T> 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<T> between(String column, Comparable value1, Comparable value2) { addCondition(column, OptionType.BETWEEN, value1, value2); return this; } /** * 匹配 in (column) * * @param column 字段 * @param value 多个比较值 * @return */ public SpecificationSupport<T> in(String column, Object... value) { addCondition(column, OptionType.IN, value); return this; } /** * 忽略某个字段属性 * * @param columns * @return */ public SpecificationSupport<T> ignoreProperty(String... columns) { ignoreColumn.addAll(Arrays.asList(columns)); return this; } /** * 忽略大小写匹配 = lower(column) * * @param columns * @return */ public SpecificationSupport<T> 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 */ @SafeVarargs private final <K> void addCondition(String column, OptionType optionType, K... value) { 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)); } } /** * and操作,创建一个消费者,依靠lambed表达式,消费一个创建好的对象,以添加到定义的队列中 * * @param and * @return */ public SpecificationSupport<T> and(Consumer<SpecificationSupport<T>> and) { if (and != null) { SpecificationSupport<T> s = SpecificationSupport.of(); and.accept(s); if (!s.isEmpty()) { AND_OR.put(OptionType.AND, s); } } return this; } /** * or操作,创建一个消费者,依靠lambed表达式,消费一个创建好的对象,以添加到定义的队列中 * * @param or * @return */ public SpecificationSupport<T> or(Consumer<SpecificationSupport<T>> or) { if (or != null) { SpecificationSupport<T> 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<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) { if (t != null){ Map<String, Field> 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<Predicate> 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<OptionType, SpecificationSupport<T>> entry : AND_OR.entrySet()) { OptionType optionType = entry.getKey(); SpecificationSupport<T> 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 */ public boolean isEmpty(){ return condition.isEmpty() && AND_OR.isEmpty(); } /** * 判断条件类型,添加到组合中 * * @param root * @param cb * @param option * @return */ @SuppressWarnings("unchecked") private Predicate getPredicate(Root<T> 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<Object> in = cb.in(cb.lower(root.get(option.getName()))); for (Object o : option.getValue()) { in.value(o.toString().toLowerCase()); } return cb.and(in); } return root.get(option.getName()).in(option.getValue()); case LE: return cb.le(root.get(option.getName()), (Number) option.getValue()[0]); case LT: Number o = null; if (option.getValue()[0] instanceof String) { o = new BigInteger((String) option.getValue()[0]); } if (o == null) { o = (Number) option.getValue()[0]; } return cb.lt(root.get(option.getName()), o); case LIKE: if (ignoreCase.contains(option.getName())) { return cb.like(cb.lower(root.get(option.getName())), "%" + option.getValue()[0].toString().toLowerCase() + "%"); } return cb.like(root.get(option.getName()), "%" + option.getValue()[0] + "%"); case STARTS_LIKE: if (ignoreCase.contains(option.getName())) { return cb.like(cb.lower(root.get(option.getName())), "%" + option.getValue()[0].toString().toLowerCase()); } return cb.like(root.get(option.getName()), "%" + option.getValue()[0]); case END_LIKE: if (ignoreCase.contains(option.getName())) { return cb.like(cb.lower(root.get(option.getName())), option.getValue()[0].toString().toLowerCase() + "%"); } return cb.like(root.get(option.getName()), option.getValue()[0] + "%"); case IS_NULL: return root.get(option.getName()).isNull(); case IS_NOT_NULL: return root.get(option.getName()).isNotNull(); case NOT_LIKE: if (ignoreCase.contains(option.getName())) { return cb.notLike(cb.lower(root.get(option.getName())), "%" + option.getValue()[0].toString().toLowerCase() + "%"); } return cb.notLike(root.get(option.getName()), "%" + option.getValue()[0] + "%"); case NOT_STARTS_LIKE: if (ignoreCase.contains(option.getName())) { return cb.notLike(cb.lower(root.get(option.getName())), "%" + option.getValue()[0].toString().toLowerCase()); } return cb.notLike(root.get(option.getName()), "%" + option.getValue()[0]); case NOT_END_LIKE: if (ignoreCase.contains(option.getName())) { return cb.notLike(cb.lower(root.get(option.getName())), option.getValue()[0].toString().toLowerCase() + "%"); } return cb.notLike(root.get(option.getName()), option.getValue()[0] + "%"); case NOT_EQ: if (ignoreCase.contains(option.getName())) { return cb.notEqual(cb.lower(root.get(option.getName())), option.getValue()[0].toString().toLowerCase()); } return cb.notEqual(root.get(option.getName()), option.getValue()[0]); case BETWEEN: return cb.between(root.get(option.getName()), (Comparable) option.getValue()[0], (Comparable) option.getValue()[1]); } return null; } private enum OptionType { AND, OR, BETWEEN, //between x1 and x2 STARTS_LIKE,//like %s END_LIKE, //like s% LIKE, //like %s% EQ, //= GT, //> LT, //< GE, //>= LE, //<= IN, //in REGEXP, //regexp(column,regex) IS_NULL, // is null IS_NOT_NULL,//is not null NOT_EQ, //!= NOT_LIKE, //not like %s% NOT_STARTS_LIKE,//not like %s NOT_END_LIKE, //not like s% IGNORE_CASE //忽略大小写 } private static class FieldOption<T> { private String name; private OptionType optionType; private T[] value; FieldOption(String name, OptionType optionType, T[] value) { this.name = name; this.optionType = optionType; this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } public OptionType getOptionType() { return optionType; } public void setOptionType(OptionType optionType) { this.optionType = optionType; } public T[] getValue() { return value; } public void setValue(T[] value) { this.value = value; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FieldOption option = (FieldOption) o; return Objects.equals(name, option.name); } @Override public int hashCode() { return Objects.hash(name); } } }
关于以上代码中用到的其他工具代码:
[code]/** * 获取对象Field属性对象集合ap * * @param clazz * @return */ public static Map<String, Field> getFieldMap(Class clazz) { Map<String, Field> result = new LinkedHashMap<String, Field>(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; }
这些你都可以复制后直接使用!
具体代码实现原理这里不再累赘,网上很多这方面的资料。
由于时间问题只看了两天的资料,所以有一些疏漏的地方轻喷,请指正。
相关文章推荐
- SpringBoot笔记6---如何读写数据库之使用JpaSpecificationExecutor读写数据库
- Spring data JPA使用Specification实现动态查询例子
- 如何建立索引,提高查询速度。 ---- 人们在使用SQL时往往会陷入一个误区,即太关注于所得的结果是否正确,而忽略了不同的实现方法之间可能存在的 性能差异,这种性能差异在大型的或是复杂的数据库环境中
- Spring data jpa 实现简单动态查询的通用Specification方法
- 如何使用JPA的nativeQuery将查询出的信息封装为对象
- spring data jpa 利用JpaSpecificationExecutor做复杂查询
- Spring Data JpaSpecificationExecutor 做复杂查询
- spring data jpa 利用JpaSpecificationExecutor做复杂查询
- SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法
- SpringBoot中使用Spring Data Jpa 实现简单的动态查询的两种方法
- nodejs自学之旅(3)—— 使用模型对象操作数据库实现简单的注册和登录
- php使用面对对象思想实现数据库简单的增删改查操作
- SpringBoot集成MyBatis(主要用来方便的进行自定义一些sql查询,主要的简单的数据库操作还是依赖于自身提供的JPA)
- Hibernate中JPA的简单使用,实现简单的对表的增删改查操作
- JpaSpecificationExecutor实现关联查询
- 如何使用dojo.query 进行DOM查询和批量操作
- 如何使用xFire开发webService返回复杂对象List
- 使用mysql函数实现多步查询中的回滚操作
- 操作必须使用一个可更新的查询;HTTP 500 - 内部服务器错误; 您未被授权查看该页;您不具备使用所提供的凭据查看该目录或页的权限;HTTP 错误 401.3 - 访问被资源 ACL 拒绝
- ORM,ASP.NET中ORM学习,ASP.NET中ORM学习心得,WEB2.0中ORM实现原理,Asp.net简单ORM示例源码详细讲解,Asp.net2.0:如何使用ObjectDataSource(配合ORM )(二)