1、简介

在我们使用JPA时,构建 Specification 查询条件时重复代码过多,而且需要大量的无效代码。

2、工具类提供的方法

2.1、自动构建规范

  /**
* 自动构建规范
*
* @param builder JapCriteriaBuilder
* @param entity 实体
* @param <V> 实体类型
* @return Specification
*/
public <V> JpaCriteriaBuilder<T> autoBuilder(JpaCriteriaBuilder<T> builder, @NotNull V entity, @NotNull Class<T> clazz) {
if (Objects.isNull(entity)) {
throw new GlobalRuntimeException("实体不能为空");
}
List<String> ignoreFields = List.of("serialVersionUID");
List<Field> fields = getDeclaredFields(clazz);
for (Field field : fields) {
if (field == null || Modifier.isStatic(field.getModifiers())) {
continue;
}
try {
field.setAccessible(Boolean.TRUE);
Object value = ReflectionUtils.getFieldValue(entity, field.getName());
if (StringUtils.isNullAndSpaceOrEmpty(value) || ignoreFields.contains(field.getName())) {
continue;
}
builder = builder.equal(field.getName(), value);
} catch (Exception e) {
LOG.error(e, "自动构建规范异常");
}
} // 分页数据
String pageSize = "pageSize", pageIndex = "pageIndex";
Object value = ReflectionUtils.getFieldValue(entity, pageIndex);
if (StringUtils.isNotNullAndEmpty(value)) {
builder = builder.page(ConvertUtils.convertInt(value));
}
// 页码大小
value = ReflectionUtils.getFieldValue(entity, pageSize);
if (StringUtils.isNotNullAndEmpty(value)) {
builder = builder.size(ConvertUtils.convertInt(value));
} // 返回结果
return builder;
}

2.2 分组或过虑字段构建规范

2.3 添加等值查询条件

  /**
* 添加等值查询条件
*
* @param field 字段
* @param value 值
* @param <V> 泛型
* @return JapCriteriaBuilder
*/
public <V> JpaCriteriaBuilder<T> equal(@NotNull String field, @NotNull V value) {
if (isValidValue(value)) {
specification = specification.and((root, query, criteriaBuilder) ->
criteriaBuilder.equal(root.get(field), value)
);
}
return this;
}

2.4 添加模糊查询条件

  /**
* 添加模糊查询条件
*
* @param field 字段
* @param value 值
* @param <V> 泛型
* @return JapCriteriaBuilder
*/
public <V> JpaCriteriaBuilder<T> like(@NotNull String field, @NotNull V value) {
if (isValidValue(value)) {
specification = specification.and((root, query, criteriaBuilder) -> {
String pattern = StringUtils.PERCENT + value + StringUtils.PERCENT;
return criteriaBuilder.like(root.get(field), pattern);
});
}
return this;
}

2.5  添加 in 查询条件

  /**
* 添加in查询条件
*
* @param field 字段
* @param values 值
* @param <V> 泛型
* @return JapCriteriaBuilder
*/
public <V> JpaCriteriaBuilder<T> in(@NotNull String field, @NotNull List<V> values) {
if (CollectUtils.isNotEmpty(values)) {
specification = specification.and((root, query, criteriaBuilder) ->
root.get(field).in(values)
);
}
return this;
}

2.6 添加大于查询条件

  /**
* 添加大于查询条件
*
* @param field 字段
* @param value 值
* @param <V> 泛型
* @return JapCriteriaBuilder
*/
public <V extends Comparable<? super V>> JpaCriteriaBuilder<T> greaterThan(@NotNull String field, @NotNull V value) {
if (isValidValue(value)) {
specification = specification.and((root, query, criteriaBuilder) ->
criteriaBuilder.greaterThan(root.get(field), value)
);
}
return this;
}

2.7 添加大于等于查询条件

  /**
* 添加大于等于查询条件
*
* @param field 字段
* @param value 值
* @param <V> 泛型
* @return JapCriteriaBuilder
*/
public <V extends Comparable<? super V>> JpaCriteriaBuilder<T> greaterThanOrEqualTo(@NotNull String field, @NotNull V value) {
if (isValidValue(value)) {
specification = specification.and((root, query, criteriaBuilder) ->
criteriaBuilder.greaterThanOrEqualTo(root.get(field), value)
);
}
return this;
}

2.8 添加小于查询条件

  /**
* 添加小于查询条件
*
* @param field 字段
* @param value 值
* @param <V> 泛型
* @return JapCriteriaBuilder
*/
public <V extends Comparable<? super V>> JpaCriteriaBuilder<T> lessThan(@NotNull String field, @NotNull V value) {
if (isValidValue(value)) {
specification = specification.and((root, query, criteriaBuilder) ->
criteriaBuilder.lessThan(root.get(field), value)
);
}
return this;
}

2.9 添加区间查询条件

  /**
* 添加区间查询条件
*
* @param field 字段
* @param from 区间起始值
* @param to 区间结束值
* @param <V> 泛型
* @return JapCriteriaBuilder
*/
public <V extends Comparable<? super V>> JpaCriteriaBuilder<T> between(@NotNull String field, @NotNull V from, V to) {
if (from != null && to != null) {
specification = specification.and((root, query, criteriaBuilder) ->
criteriaBuilder.between(root.get(field), from, to)
);
}
return this;
}

2.10 添加空查询条件

  /**
* 添加空查询条件
*
* @param field 字段
* @return JapCriteriaBuilder
*/
public JpaCriteriaBuilder<T> isNull(String field) {
specification = specification.and((root, query, criteriaBuilder) ->
criteriaBuilder.isNull(root.get(field))
);
return this;
}

2.11 添加非空查询条件

  /**
* 添加非空查询条件
*
* @param field 字段
* @return JapCriteriaBuilder
*/
public JpaCriteriaBuilder<T> isNotNull(String field) {
specification = specification.and((root, query, criteriaBuilder) ->
criteriaBuilder.isNotNull(root.get(field))
);
return this;
}

2.12 添加关联查询条件

  /**
* 添加关联查询条件
*
* @param joinField 关联字段
* @param field 字段
* @param value 值
* @param joinType 关联类型
* @param <X> 关联实体类型
* @return JapCriteriaBuilder
*/
public <X> JpaCriteriaBuilder<T> joinEqual(String joinField, @NotNull String field, @NotNull Object value, JoinType joinType) {
if (isValidValue(value)) {
specification = specification.and((root, query, criteriaBuilder) -> {
Join<T, X> join = root.join(joinField, joinType);
return criteriaBuilder.equal(join.get(field), value);
});
}
return this;
}

2.13 添加自定义查询条件

  /**
* 添加自定义查询条件
*
* @param condition 自定义查询条件
* @return JapCriteriaBuilder
*/
public JpaCriteriaBuilder<T> add(Function<Root<T>, Predicate> condition) {
specification = specification.and((root, query, criteriaBuilder) -> {
Predicate predicate = condition.apply(root);
return criteriaBuilder.and(predicate);
});
return this;
}

2.14 或查询

  /**
* 或查询
*
* @param otherBuilder 其他查询条件
* @return JapCriteriaBuilder
*/
public JpaCriteriaBuilder<T> or(JpaCriteriaBuilder<T>... otherBuilder) {
// 创建一个新的 Specification 来存储合并后的查询条件
Specification<T> combinedSpecification = Specification.where(specification); // 遍历所有传入的 JapCriteriaBuilder
for (JpaCriteriaBuilder<T> builder : otherBuilder) {
combinedSpecification = combinedSpecification.or(builder.build());
} // 更新当前的 specification 为合并后的结果
specification = combinedSpecification;
return this;
}

2.15 页码

  /**
* 页码
*
* @param page 页码
* @return JapCriteriaBuilder
*/
public JpaCriteriaBuilder<T> page(int page) {
this.pageNumber = page;
return this;
}

2.16 每页大小

  /**
* 每页大小
*
* @param size 每页大小
* @return JapCriteriaBuilder
*/
public JpaCriteriaBuilder<T> size(int size) {
this.pageSize = size;
return this;
}

2.17 排序

  /**
* 排序
*
* @param field 字段
* @param direction 排序方向
* @return JapCriteriaBuilder
*/
public JpaCriteriaBuilder<T> orderBy(@NotNull String field, @NotNull Sort.Direction direction) {
if (direction != null) {
rawSortList.add(Sort.Order.by(field).with(direction));
}
return this;
}

2.18 构建分页查询条件

  /**
* 构建分页查询条件
*
* @return Pageable
*/
public Pageable toPageable() {
if (CollectUtils.isEmpty(rawSortList)) {
throw new GlobalRuntimeException("分页查询必须指定排序条件");
}
return PageRequest.of(
Objects.requireNonNullElse(pageNumber, IntegerConsts.ZERO),
Objects.requireNonNullElse(pageSize, IntegerConsts.FIFTEEN),
Sort.by(rawSortList)
);
}

2.19 构建查询条件

  /**
* 构建查询条件
*
* @return Specification
*/
public Specification<T> build() {
return (root, query, cb) -> {
// 应用排序条件
if (!rawSortList.isEmpty()) {
List<Order> orders = rawSortList.stream()
.map(sortOrder -> {
Path<Object> path = root.get(sortOrder.getProperty());
return sortOrder.getDirection().isAscending() ? cb.asc(path) : cb.desc(path);
})
.collect(Collectors.toList());
query.orderBy(orders);
}
// 生成查询条件
return specification.toPredicate(root, query, cb);
};
}

3 使用示例

3.1 使用自动构建规范

    // 查询条件
JpaCriteriaBuilder<WikiLicenseEntity> builder = JpaCriteriaBuilder.Builder();
builder = builder.autoBuilder(builder, license); // 排序
builder = builder.orderBy(WikiLicenseDto.ADD_TIME, Sort.Direction.DESC); // 分页
builder = builder .page(license.getPageIndex() - IntegerConsts.ONE)
.size(license.getPageSize()); // 查询分页数据
Page<WikiLicenseEntity> page = wikiLicenseRepository.findAll(builder.build(), builder.toPageable());

3.2 自定义添加查询条件

    // 查询条件
JpaCriteriaBuilder<WikiLicenseEntity> builder = JpaCriteriaBuilder.Builder();
builder.equal(WikiLicenseDto.DELETED, IntegerConsts.ZERO);
builder.equal(WikiLicenseDto.STATUS, IntegerConsts.ONE); JpaCriteriaBuilder<WikiLicenseEntity> builderKey = JpaCriteriaBuilder.Builder();
builderKey = builderKey.equal(WikiLicenseDto.KEY, license.getKey()); JpaCriteriaBuilder<WikiLicenseEntity> builderName = JpaCriteriaBuilder.Builder();
builderName = builderName.equal(WikiLicenseDto.NAME, license.getName());
// 添加或查询
builder = builder.or(builderKey, builderName); // 查询数据
List<WikiLicenseEntity> licenseList = wikiLicenseRepository.findAll(builder.build());

4 总结

更多使用或者有更好的方法欢迎连续博主,完整的代码可查看博主开源的框架:维基框架

Gitee:https://gitee.com/cdkjframework/wiki-framework

Github:https://github.com/cdkjframework/wiki-framework

若觉得博主的项目还不错,希望你能给博主 star及fork。如果有其他需要了解的内容请留言,看到后会及时回复。

Spring Boot Jpa封装快速构建Specification、OrderBy、Pageable的查询条件的更多相关文章

  1. SPring boot jpa 封装查询条件

    最近使用spring data jpa做了两个项目,对于动态查询的不友好做了个类似hibernate的封装,记录也分享下 首先定义一个所有条件的容器,继承Specification /** * 定义一 ...

  2. 用 Spring Boot 和 MybatisPlus 快速构建项目

    自动生成 public class MPGenerator { public static void main(String[] args) { AutoGenerator autoGenerator ...

  3. Spring Boot(五):Spring Boot Jpa 的使用

    在上篇文章Spring Boot(二):Web 综合开发中简单介绍了一下 Spring Boot Jpa 的基础性使用,这篇文章将更加全面的介绍 Spring Boot Jpa 常见用法以及注意事项. ...

  4. Spring Boot 2.x 快速入门(下)HelloWorld示例详解

    上篇 Spring Boot 2.x 快速入门(上)HelloWorld示例 进行了Sprint Boot的快速入门,以实际的示例代码来练手,总比光看书要强很多嘛,最好的就是边看.边写.边记.边展示. ...

  5. Spring Boot Jpa 的使用

    Spring Boot Jpa 介绍 首先了解 Jpa 是什么? Jpa (Java Persistence API) 是 Sun 官方提出的 Java 持久化规范.它为 Java 开发人员提供了一种 ...

  6. (转)Spring Boot(五):Spring Boot Jpa 的使用

    http://www.ityouknow.com/springboot/2016/08/20/spring-boot-jpa.html 在上篇文章Spring Boot(二):Web 综合开发中简单介 ...

  7. spring boot☞Swagger2文档构建及单元测试

    首先,回顾并详细说明一下在快速入门中使用的@Controller.@RestController.@RequestMapping注解.如果您对Spring MVC不熟悉并且还没有尝试过快速入门案例,建 ...

  8. Spring Boot中使用Swagger2构建强大的RESTful API文档

    由于Spring Boot能够快速开发.便捷部署等特性,相信有很大一部分Spring Boot的用户会用来构建RESTful API.而我们构建RESTful API的目的通常都是由于多终端的原因,这 ...

  9. Spring Boot中使用Swagger2构建API文档

    程序员都很希望别人能写技术文档,自己却很不愿意写文档.因为接口数量繁多,并且充满业务细节,写文档需要花大量的时间去处理格式排版,代码修改后还需要同步修改文档,经常因为项目时间紧等原因导致文档滞后于代码 ...

  10. spring boot / cloud (十七) 快速搭建注册中心和配置中心

    spring boot / cloud (十七) 快速搭建注册中心和配置中心 本文将使用spring cloud的eureka和config server来搭建. 然后搭建的模式,有很多种,本文主要聊 ...

随机推荐

  1. ORACLE 分页排序后的数据重复或缺失问题

    今天一大早业务人员就反映说用户导出的订单数据,有好几单是重复,并且缺失了某一单. 第一步:查询数据表.表里实际数据没有重复,也没有缺失.那么就可能是导出过程出错了(因为是异步分页导出,所以最先怀疑这部 ...

  2. 如何正确配置 .gitignore 以忽略特定文件夹下的文件(除指定子文件夹外)

    在使用 Git 进行版本控制时,.gitignore 文件是一个非常有用的工具,可以帮助我们忽略不需要跟踪的文件或文件夹.然而,有时我们需要忽略某个文件夹下的所有内容,但保留其中的某个子文件夹.本文将 ...

  3. WinForm 多线程+委托来防止界面假死

    参考: http://www.cnblogs.com/xpvincent/archive/2013/08/19/3268001.html 当有大量数据需要计算.显示在界面或者调用sleep函数时,容易 ...

  4. 【忍者算法】从生活到代码:解密链表大数相加的美妙算法|LeetCode第2题"两数相加"

    从生活到代码:解密链表大数相加的美妙算法 从超市收银说起 想象你是一个超市收银员,正在计算两位顾客的购物总和.每位顾客的商品都按照从个位到高位的顺序摆放(比如54元就是先放4元商品,再放50元商品). ...

  5. QT5笔记:17. QComboBox和QPlainTextEdit

    例子 #include "widget.h" #include "ui_widget.h" #include <QTextBlock> Widget ...

  6. DeepSeek “源神”启动!「GitHub 热点速览」

    ​上周,DeepSeek 官方宣布将陆续发布 5 个开源项目.本周一开源社区就迎来了首发之作--FlashMLA!该项目开源后,不到一天 Star 数已突破 6k,并且还在以惊人的速度持续飙升. Gi ...

  7. 奥特曼框架autMan对接微信公众号的详细教程

    1.简介 微信公众号分为订阅号(个人)和服务号(公司),个人是可以申请的哈.具体怎么申请参见官方文档:https://kf.qq.com/faq/120911VrYVrA151009eIrYvy.ht ...

  8. Spark - [03] 资源调度模式

    题记部分 一.Local模式 1.1.概述 Local模式就是运行在一台计算机上的模式,通常就是用于在本机上练手和测试的. 可以通过以下几种方式设置Master (1)local:所欲计算都运行在一个 ...

  9. Archlinux 更新失败之驱动与 Xorg 配置错误

    Archlinux系统更新是滚动更新,所以更新失败又被叫做"滚挂了" 此次滚挂发生在1月27日,过了那么久了才想起来该记录了-- 现象 滚挂的现象是,能够进系统,但是笔记本电脑自带 ...

  10. Markdown 编写技巧汇总(二)

    继续上篇汇总 附-上篇汇总,接着做更加高级一点的应用技巧. [1]列表与引言嵌套 两者嵌套,如: > 我是一行文本 > 1. 文本1 > 2. 文本2 > 1. 文本 > ...