1.pom文件中添加如下配置

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.5.1.RELEASE</version>
</dependency>

2.BaseQuery.java

package cn.springjpa.query;

import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map; /**
* @ClassName BaseQuery
* @Description
*/
public abstract class BaseQuery<T> { // start from 0
protected int pageIndex = 0;
protected int pageSize = 10;
private static Map<Class, List<Field>> fieldCache = new HashMap<>(); /**
* 将查询转换成Specification
* @return
*/
public abstract Specification<T> toSpec(); //JPA分页查询类
public Pageable toPageable() {
return new PageRequest(pageIndex, pageSize);
} //JPA分页查询类,带排序条件
public Pageable toPageable(Sort sort) {
return new PageRequest(pageIndex, pageSize, sort);
} //动态查询and连接
protected Specification<T> toSpecWithAnd() {
return this.toSpecWithLogicType("and");
} //动态查询or连接
protected Specification<T> toSpecWithOr() {
return this.toSpecWithLogicType("or");
} //logicType or/and
@SuppressWarnings({ "rawtypes", "unchecked" })
private Specification<T> toSpecWithLogicType(final String logicType) {
final BaseQuery outerThis = this;
//封装条件查询对象Specification
Specification<T> specification = new Specification<T>() {
@Override
// Root 用于获取属性字段,CriteriaQuery可以用于简单条件查询,CriteriaBuilder 用于构造复杂条件查询
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Class clazz = outerThis.getClass();
//判断缓存中是否已经存在,存在不需要再次生成,不存在需要重新生成
List<Field> fields = fieldCache.get(clazz);
if (fields == null) {
//获取查询类Query的所有字段,包括父类字段
fields = getAllFieldsWithRoot(clazz);
fieldCache.put(clazz, fields);
}
List<Predicate> predicates = new ArrayList<>(fields.size());
for (Field field : fields) {
//获取字段上的@QueryWord注解
QueryCondition qw = field.getAnnotation(QueryCondition.class);
if (qw == null)
continue;
// 获取字段名
String column = qw.column();
//如果主注解上colume为默认值"",则以field为准
if (column.equals(""))
column = field.getName();
field.setAccessible(true);
try {
// nullable
Object value = field.get(outerThis);
//如果值为null,注解未标注nullable,跳过
if (value == null && !qw.nullable())
continue;
// can be empty
if (value != null && String.class.isAssignableFrom(value.getClass())) {
String s = (String) value;
//如果值为"",且注解未标注empty,跳过
if (s.equals("") && !qw.empty())
continue;
}
//通过注解上func属性,构建路径表达式
Path path = root.get(column);
switch (qw.func()) {
case equal:
predicates.add(cb.equal(path, value));
break;
case like:
predicates.add(cb.like(path, "%" + value + "%"));
break;
case gt:
predicates.add(cb.gt(path, (Number) value));
break;
case lt:
predicates.add(cb.lt(path, (Number) value));
break;
case ge:
predicates.add(cb.ge(path, (Number) value));
break;
case le:
predicates.add(cb.le(path, (Number) value));
break;
case notEqual:
predicates.add(cb.notEqual(path, value));
break;
case notLike:
predicates.add(cb.notLike(path, "%" + value + "%"));
break;
case greaterThan:
predicates.add(cb.greaterThan(path, (Comparable) value));
break;
case greaterThanOrEqualTo:
predicates.add(cb.greaterThanOrEqualTo(path, (Comparable) value));
break;
case lessThan:
predicates.add(cb.lessThan(path, (Comparable) value));
break;
case lessThanOrEqualTo:
predicates.add(cb.lessThanOrEqualTo(path, (Comparable) value));
break;
case between:
switch (qw.type()) {
case datetime:
List<Date> dateList = (List<Date>) value;
predicates.add(cb.between(path, dateList.get(0), dateList.get(1)));
break;
case number_long:
List<Long> longList = (List<Long>) value;
predicates.add(cb.between(path, longList.get(0), longList.get(1)));
break;
case number_integer:
List<Integer> integerList = (List<Integer>) value;
predicates.add(cb.between(path, integerList.get(0), integerList.get(1)));
break;
}
}
} catch (Exception e) {
continue;
}
}
Predicate p = null;
if (logicType == null || logicType.equals("") || logicType.equals("and")) {
p = cb.and(predicates.toArray(new Predicate[predicates.size()]));//and连接
} else if (logicType.equals("or")) {
p = cb.or(predicates.toArray(new Predicate[predicates.size()]));//or连接
}
return p;
}
};
return specification;
} //获取类clazz的所有Field,包括其父类的Field
private List<Field> getAllFieldsWithRoot(Class<?> clazz) {
List<Field> fieldList = new ArrayList<>();
Field[] dFields = clazz.getDeclaredFields();//获取本类所有字段
if (null != dFields && dFields.length > 0)
fieldList.addAll(Arrays.asList(dFields));
// 若父类是Object,则直接返回当前Field列表
Class<?> superClass = clazz.getSuperclass();
if (superClass == Object.class) return Arrays.asList(dFields);
// 递归查询父类的field列表
List<Field> superFields = getAllFieldsWithRoot(superClass);
if (null != superFields && !superFields.isEmpty()) {
for (Field field : superFields) {
if (!fieldList.contains(field)) {
fieldList.add(field);
}
}
}
return fieldList;
} public int getPageIndex() {
return pageIndex;
} public void setPageIndex(int pageIndex) {
this.pageIndex = pageIndex;
} public int getPageSize() {
return pageSize;
} public void setPageSize(int pageSize) {
this.pageSize = pageSize;
} }

3.BetweenType.java

package cn.springjpa.query;

/**
* @ClassName BetweenType
* @Description between...and... 查询语句标识,
*/
public enum BetweenType { datetime,
number_long,
number_integer
}

4.MatchType.java

package cn.springjpa.query;

/**
* @ClassName MatchType
* @Description 列出所有的拼接条件
*/
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
between, // between value1 and value2 ,Type is Date // 下面四个用于可比较类型(Comparable)的比较
greaterThan, // field > value
greaterThanOrEqualTo, // field >= value
lessThan, // field < value
lessThanOrEqualTo // field <= value }

5.QueryCondition.java

package cn.springjpa.query;

import java.lang.annotation.*;

/**
* @ClassName QueryCondition
* @Description 自定义注解,用来标识字段
*/
@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 empty() default false; // between...and... 查询语句标识, 0时间 1数字类型
BetweenType type() default BetweenType.datetime;
}

6.如何使用

(1) 将以上四个工具类放入自己的项目中

(2) 新建自己的查询实体bean,可以参考如下的例子

/**
* @ClassName CertReqQuery
* @Description 查询证书请求条件封装
*/
public class CertReqQuery extends BaseQuery<CertReqEntity> {
@QueryCondition(func= MatchType.equal)
private Integer certReqStatus;
@QueryCondition(func= MatchType.equal)
private Long accountId; public CertReqQuery() {
} public CertReqQuery(Integer certReqStatus, Long accountId) {
this.certReqStatus = certReqStatus;
this.accountId = accountId;
} public Integer getCertReqStatus() {
return certReqStatus;
} public void setCertReqStatus(Integer certReqStatus) {
this.certReqStatus = certReqStatus;
} public Long getAccountId() {
return accountId;
} public void setAccountId(Long accountId) {
this.accountId = accountId;
} @Override
public Specification<CertReqEntity> toSpec() {
// 也可以写自定义方法
return super.toSpecWithAnd();
}
}

(3) 接下来在service层调用即可,参考如下示例:

//设置查询条件对象

CertReqQuery certReqQuery = new CertReqQuery(1, 123456L);

certReqQuery.setPageIndex(0;

certReqQuery.setPageSize(10);

return repo.findAll(certReqQuery.toSpec(), certReqQuery.toPageable(sort));

spring-boot-jpa 自定义查询工具类的更多相关文章

  1. spring boot 结合Redis 实现工具类

    自己整理了 spring boot 结合 Redis 的工具类引入依赖 <dependency> <groupId>org.springframework.boot</g ...

  2. Spring Boot JPA的查询语句

    文章目录 准备工作 Containing, Contains, IsContaining 和 Like StartsWith EndsWith 大小写不敏感 Not @Query Spring Boo ...

  3. Spring Boot 入门系列(二十七)使用Spring Data JPA 自定义查询如此简单,完全不需要写SQL!

    前面讲了Spring Boot 整合Spring Boot JPA,实现JPA 的增.删.改.查的功能.JPA使用非常简单,只需继承JpaRepository ,无需任何数据访问层和sql语句即可实现 ...

  4. 【Spring Data 系列学习】Spring Data JPA 自定义查询,分页,排序,条件查询

    Spring Boot Jpa 默认提供 CURD 的方法等方法,在日常中往往时无法满足我们业务的要求,本章节通过自定义简单查询案例进行讲解. 快速上手 项目中的pom.xml.application ...

  5. spring boot 文件上传工具类(bug 已修改)

    以前的文件上传都是之前前辈写的,现在自己来写一个,大家可以看看,有什么问题可以在评论中提出来. 写的这个文件上传是在spring boot 2.0中测试的,测试了,可以正常上传,下面贴代码 第一步:引 ...

  6. SPring boot jpa 封装查询条件

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

  7. spring boot jpa 复杂查询 动态查询 连接and和or 模糊查询 分页查询

    最近项目中用到了jpa,刚接触的时候有些激动,以前的到层忽然不用写sql不用去自己实现了,只是取个方法名就实现了,太惊艳了,惊为天人,但是慢慢的就发现不是这么回事了,在动态查询的时候,不知道怎么操作了 ...

  8. Spring boot JPA 用自定义主键策略 生成自定义主键ID

    最近学习Spring boot JPA 学习过程解决的一些问题写成随笔,大家一起成长.这次遇到自定义主键的问题 package javax.persistence; public enum Gener ...

  9. spring boot JPA中实体类常用注解

    spring boot jpa中的注解很多,参数也比较多.没必要全部记住,但是经常查看官方文档也比较麻烦,记录一下一些常用的注解.通过一些具体的例子来帮助记忆. @Entity @Table(name ...

随机推荐

  1. python算法之冒泡排序

    目录 python之冒泡排序 算法原理 算法分析 代码实现 总结 python之冒泡排序 概念: 重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小.首字母从A到Z)错误就 ...

  2. Python jieba库的使用说明

    1.jieba库基本介绍 (1).jieba库概述 jieba是优秀的中文分词第三方库 - 中文文本需要通过分词获得单个的词语         - jieba是优秀的中文分词第三方库,需要额外安装 - ...

  3. Linux man C++ 库函数

    默认情况下,linux是的man是不能查阅C++的标准库函数的,这个很不方便,那有没有办法可以直接man C++标准库函数呢? 当然有,不过要自己动手,自己动手,才能丰衣足食! 1. 下载安装manp ...

  4. 添加右键使用 SublimeText 打开

    最近修改了系统,重新安装 SublimeText 但是在安装的时候忘记设置右键使用 SublimeText 打开,所以就需要写注册表. 可以复制下面的代码到一个记事本,然后保存为 sublime_ad ...

  5. python多线程之threading模块

    threading模块中的对象 其中除了Thread对象以外,还有许多跟同步相关的对象 threading模块支持守护线程的机制 Thread对象 直接调用法 import threading imp ...

  6. 使用EXCEL 完成分组统计

    表testTable 有以下数据,要统计各个电视剧的数量.在开发人员看来,使用sql操作完成select name,count(*) from TestTable where 1=1 group by ...

  7. CodeForces765B

    B. Code obfuscation time limit per test:2 seconds memory limit per test:512 megabytes input:standard ...

  8. JS 自定义对象 属性

    js自定义对象 一,概述 在Java语言中,我们可以定义自己的类,并根据这些类创建对象来使用,在Javascript中,我们也可以定义自己的类,例如定义User类.Hashtable类等等. 目前在J ...

  9. CF696C PLEASE

    矩阵快速幂+扩展欧拉定理 对于一个矩阵\(A\),我们有\(A^n \equiv A^{n\% \phi(m)+\phi(m)}(\%m)\) 经过简单的列举或推导可得 设目前进行了\(x\)轮,\( ...

  10. 在vue-cli中使用layer中的layData日期组件

    有朋友问我怎么在vue-cli项目中使用layui中的layData组件,有时间从网上查了下写下篇文章. 1.首先去layData官网把文件包下载下来,解压出来的laydate文件夹整个放在vue-c ...