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. MongoDB之集合管理一

    最近的几篇博客都是关于MongoDB的,虽然个人感觉也没多少知识点,但没想到竟然有转载我的博客的,不管有经过我同意还是没经过我同意,说明写的应该还是有价值的,这也是我写博客的一个动力之一吧.上一博客学 ...

  2. hadoop2.x学习笔记(一):YARN

    一.YARN产生的背景 MapReduce1.x存在的问题:单点故障&节点压力大不易扩展. 资源利用率&成本 催生了YARN的诞生  不同计算框架可以共享同一个HDFS集群上的数据,享 ...

  3. 使用jdk压缩war包

    首先安装jdk 压缩 ..../jdk/bin/jar -cvf file.war file 解压 ..../jdk/bin/jar -xvf file.war

  4. Sqlserver2008及以上使用全文索引排除干扰词

    关于SQLServer2008全文索引干扰词 使用SQL SERVER 2008全文索引进行搜索 contains(Keywords, '"xx of xx*"')  (注意是后面 ...

  5. 【转】类找不到总结java.lang.ClassNotFoundException

    (1)org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot load JDBC driver class 'com.microsoft.sqls ...

  6. Javascript实现图片翻转

    使用Js的对象属性能实现简单的翻转效果.通过onMouseover事件和onMouseOut事件就能实现鼠标移到链接上和移开链接发生反应.给两个图片加上链接实现都能翻转的效果. reverse.htm ...

  7. CSS3 @font-face实现颜色大小可控的三角效果——张鑫旭

    一.我之前介绍过的三角实现效果回顾 这里所说的三角效果之等腰直角三角形效果(等边三角形有现成字符实现,没什么好说的:还有图片实现三角众人皆知,不予以说明): 1. 字符实现三角效果关于字符实现三角我早 ...

  8. Code Signal_练习题_reverseParentheses

    You have a string s that consists of English letters, punctuation marks, whitespace characters, and ...

  9. iview select下拉bug

    1场景:弹框内有一个下拉组件(支持搜索),当选择完数据后弹框关闭,再次打开后,下拉框内的数据是刚才选中的数据.原因:分析后觉得是搜索内容没有清空,导致下拉的数据只有一个解决:调用下setQuery方法 ...

  10. ID3决策树算法实现(Python版)

    # -*- coding:utf-8 -*- from numpy import * import numpy as np import pandas as pd from math import l ...