github:https://github.com/peterowang/spring-data-jpa-demo

单一实体的动态查询:

@Service
public class AdvancedUserInfoService{
@Autowired
UserInfoRepository userInfoRepository;
/**
* 简单分页排序查询
*/
public Page<UserInfo> pageList(int pageNo,int pageSize){
Sort sort = new Sort(Sort.Direction.DESC, "id");
Pageable able = new PageRequest(pageNo, pageSize, sort);
return userInfoRepository.findAll(able);
} /**
* 复杂动态多条件查询
* @param username
* @param password
* @param id
* @return
*/
public List<UserInfo> listDynamic(final String username,final String password,final Integer id){
Specification<UserInfo> sf = new Specification<UserInfo>() {
List<Predicate> list = new ArrayList<>();
@Override
public Predicate toPredicate(Root<UserInfo> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
/* Predicate p1 = cb.like(root.get("name").as(String.class), "%"+um.getName()+"%");
Predicate p2 = cb.equal(root.get("uuid").as(Integer.class), um.getUuid());
Predicate p3 = cb.gt(root.get("age").as(Integer.class), um.getAge());
//把Predicate应用到CriteriaQuery中去,因为还可以给CriteriaQuery添加其他的功能,比如排序、分组啥的
query.where(cb.and(p3,cb.or(p1,p2)));//where p3 and (p1 or p2)
//添加排序的功能
query.orderBy(cb.desc(root.get("uuid").as(Integer.class)));
return query.getRestriction();*/
List<Predicate> list = new ArrayList<>();
if(!StringUtils.isEmpty(username)){
list.add(criteriaBuilder.like(root.get("username").as(String.class), "%" + username + "%"));
}
if(!StringUtils.isEmpty(password)){
list.add(criteriaBuilder.isNotNull(root.get("password").as(String.class)));
}
if(id!=null){
list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("id").as(Integer.class),id));
}
Predicate[] pd = new Predicate[list.size()];
criteriaQuery.where(list.toArray(pd));
criteriaQuery.orderBy(criteriaBuilder.desc(root.get("id").as(Integer.class)));
return criteriaQuery.getRestriction();
}
} ;
return userInfoRepository.findAll(sf);
}
public Page<UserInfo> pageDynamic(final String username,final String password,final Integer id1,
final Integer id2,final Integer pageNo,final Integer pageSize){
return userInfoRepository.findAll(new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
Predicate p1=null;Predicate p2=null; Predicate p3=null;
if(StringUtils.isNotEmpty(username)){
p1 = criteriaBuilder.equal(root.get("username").as(String.class),username);
}
if(StringUtils.isNotEmpty(password)){
p2 = criteriaBuilder.equal(root.get("password").as(String.class), password);
}
if(id1!=null&&id2!=null){
p3 = criteriaBuilder.between(root.get("id").as(Integer.class), id1, id2);
}
criteriaQuery.where(criteriaBuilder.and(p1,criteriaBuilder.or(p2,p3)));
return criteriaQuery.getRestriction();
}
},new PageRequest(pageNo,pageSize,new Sort(Sort.Direction.DESC,"id")));
}
}

Spring Data JPA已经帮助我们很大程度上简化了我们的查询操作,我们甚至只要写一个接口,然后单纯的写一些方法就可以完成各式各样的查询,但是对于我们程序设计人员而言,总希望所有的查询变得更加的简单方便,为了给程序人员进行再一次的封装,Spring Data JPA提供了Specification的方式进行查询,在前面的内容已经演示过这种查询了,但是,我们在使用的过程中发现这种查询异常的繁琐和复杂,接下来的内容就是我们有效的对Specification进行封装来快速实现一些简单的查询操作。当然如果涉及到更为复杂的操作,依然建议写个方法来自己实现。

封装自己的Specification的实现有很多种方法,我这里只给出了相对简单的一种,而且并没有考虑太复杂的查询,个人感觉过于复杂的查询还不如直接使用SQL或者HQL来处理方便,以下是几个比较重要的类:

SpecificationOperator表示操作符类,用来确定查询条件和值。

package com.example.demo.SpecificationUtil;

/**
* Created by BFD-593 on 2017/8/17.
* 操作符类,这个类中存储了键值对和操作符号,另外存储了连接下一个条件的类型是and还是or
* 创建时通过 id>=7,其中id就是key,>=就是oper操作符,7就是value
* 特殊的自定义几个操作符(:表示like %v%,l:表示v%,:l表示%v)
*/
public class SpecificationOperator {
/**
* 操作符的key,如查询时的name,id之类
*/
private String key;
/**
* 操作符的value,具体要查询的值
*/
private Object value;
/**
* 操作符,自己定义的一组操作符,用来方便查询
*/
private String oper;
/**
* 连接的方式:and或者or
*/
private String join; public SpecificationOperator(String key, Object value, String oper, String join) {
this.key = key;
this.value = value;
this.oper = oper;
this.join = join;
}
public SpecificationOperator() { } public String getKey() {
return key;
} public void setKey(String key) {
this.key = key;
} public Object getValue() {
return value;
} public void setValue(Object value) {
this.value = value;
} public String getOper() {
return oper;
} public void setOper(String oper) {
this.oper = oper;
} public String getJoin() {
return join;
} public void setJoin(String join) {
this.join = join;
}
}
接下来创建SimpleSpecification来实现Specification接口,并且根据条件生成Specification对象,因为在最后查询的时候需要这个对象
package com.example.demo.SpecificationUtil;

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification; import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.List; /**
* 创建SimpleSpecification来实现Specification接口,
* 并且根据条件生成Specification对象,因为在最后查询的时候需要这个对象
* SimpleSpecification是核心类型,
* 用来根据条件生成Specification对象,这个SimpleSpecification直接存储了具体的查询条件。
* Created by BFD-593 on 2017/8/17.
*/
public class SimpleSpecification<T> implements Specification<T> {
/**
* 查询的条件列表,是一组列表
* */
private List<SpecificationOperator> opers; public SimpleSpecification(List<SpecificationOperator> opers){
this.opers=opers;
} @Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
int index = 0;
Predicate resultPre = null;
for(SpecificationOperator so :opers){
if(index++==0){//第一次index=0 index++是先赋值再加
resultPre = generatePredicate(root, criteriaBuilder, so);
continue;
}
Predicate pre = generatePredicate(root, criteriaBuilder, so);
if(pre==null)continue;
if("and".equalsIgnoreCase(so.getJoin())){
resultPre = criteriaBuilder.and(resultPre, pre);
}else if("or".equalsIgnoreCase(so.getJoin())){
resultPre = criteriaBuilder.or(resultPre, pre);
}
}
return resultPre;
} private Predicate generatePredicate(Root<T> root,CriteriaBuilder criteriaBuilder,SpecificationOperator so){
if(so!=null&&StringUtils.isNotEmpty(so.getOper())){
if("=".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){
return criteriaBuilder.equal(root.get(so.getKey()), so.getValue());
}else if(">=".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){
return criteriaBuilder.ge(root.get(so.getKey()).as(Number.class),(Number) so.getValue());
}else if("<=".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){
return criteriaBuilder.le(root.get(so.getKey()).as(Number.class),(Number)so.getValue());
}else if(">".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){
return criteriaBuilder.gt(root.get(so.getKey()).as(Number.class), (Number) so.getValue());
}else if("<".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){
return criteriaBuilder.lt(root.get(so.getKey()).as(Number.class), (Number) so.getValue());
}else if(":".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){
return criteriaBuilder.like(root.get(so.getKey()).as(String.class), "%" + so.getValue() + "%");
}else if(":l".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){
return criteriaBuilder.like(root.get(so.getKey()).as(String.class), "%" + so.getValue());
}else if("l:".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){
return criteriaBuilder.like(root.get(so.getKey()).as(String.class), so.getValue() + "%");
}else if("null".equalsIgnoreCase(so.getOper())){
return criteriaBuilder.isNull(root.get(so.getKey()));
}else if("!null".equalsIgnoreCase(so.getOper())){
return criteriaBuilder.isNotNull(root.get(so.getKey()));
}else if("!=".equalsIgnoreCase(so.getOper())&&so.getValue()!=null){
return criteriaBuilder.notEqual(root.get(so.getKey()), so.getValue());
}
}
return null;
}
}

SimpleSpecification是核心类型,用来根据条件生成Specification对象,这个SimpleSpecification直接存储了具体的查询条件。

最后我们创建一个SimpleSpecificationBuilder来具体创建SimpleSpecification,这里为了方便调用简单进行了一下设计。

package com.example.demo.SpecificationUtil;

import com.google.common.collect.Lists;
import org.springframework.data.jpa.domain.Specification; import java.util.List; /**
* 创建一个SimpleSpecificationBuilder来具体创建SimpleSpecification,
* 这里为了方便调用简单进行了一下设计。
* Created by BFD-593 on 2017/8/17.
*/
public class SimpleSpecificationBuilder<T> {
/**
* 条件列表
*/
private List<SpecificationOperator> opers;
/**
* 构造函数,初始化的条件是and
*/
public SimpleSpecificationBuilder(String key,String oper,Object value,String join){
SpecificationOperator so = new SpecificationOperator(key, value, oper, join);
opers = Lists.newArrayList();
opers.add(so);
} /**
* 构造,初始化无条件
*/
public SimpleSpecificationBuilder(){
opers = Lists.newArrayList();
} /**
* 往list中填加条件
* @param key
* @param oper
* @param value
* @param join
* @return
*/
public SimpleSpecificationBuilder add(String key,String oper,Object value,String join){
SpecificationOperator so = new SpecificationOperator(key, value, oper, join);
opers.add(so);
return this;
} /**
* 填加一个and条件
* @param key
* @param oper
* @param value
* @return
*/
public SimpleSpecificationBuilder and(String key,String oper,Object value){
return this.add(key, oper, value, "and");
} /**
* 填加一个or条件
* @param key
* @param oper
* @param value
* @return
*/
public SimpleSpecificationBuilder or(String key,String oper,Object value){
return this.add(key, oper, value, "or");
}
/**
* 触发SimpleSpecification并返回Specification
*/
public Specification getSpecification(){
Specification<T> sp = new SimpleSpecification<T>(opers);
return sp;
}
}
测试:
/**
* 在多条件动态查询时需要继承JpaSpecificationExecutor接口
* JpaSpecificationExecutor可以通过findAll方法传入SimpleSpecification来进行查询
* Created by BFD-593 on 2017/8/16.
*/
public interface RoleRepository extends BaseRepository<Role,Integer>,JpaSpecificationExecutor<Role> { }
/**
* 测试封装的specification
* 实现简单风格的动态查询
* id < id and roleName like %roleName% or id>id and roleName like roleName%的动态查询
* 某个参数为空时,就不使用该参数所在的条件。
* @param roleName
* @param id
* @return
*/
public List<Role> spe(String roleName,Integer id) {
return roleRepository.findAll(new SimpleSpecificationBuilder<Role>().
and("id", "<", id).
and("roleName",":",roleName).
or("id",">",id).
and("roleName","l:",roleName).
getSpecification());
}
												

spring data jpa封装specification实现简单风格的动态查询的更多相关文章

  1. 使用Spring Data JPA的Specification构建数据库查询

    Spring Data JPA最为优秀的特性就是可以通过自定义方法名称生成查询来轻松创建查询SQL.Spring Data JPA提供了一个Repository编程模型,最简单的方式就是通过扩展Jpa ...

  2. Spring Data JPA 多个实体类表联合视图查询

    Spring Data JPA 查询数据库时,如果两个表有关联,那么就设个外键,在查询的时候用Specification创建Join 查询便可.但是只支持左连接,不支持右连接,虽说左右连接反过来就能实 ...

  3. Spring Data Jpa:分页、Specification、Criteria

    分页的主要接口与类 PagingAndSortingRepository 继承自 CrudRepository 接口,提供了排序以及分页查询能力,提供了两个方法 Iterable<T> f ...

  4. Spring Data JPA简单使用

    用Spring Data JPA操作数据库 这份教程教你用Spring Data JPA从关系数据库mysql中存储和提取数据.总结来自https://spring.io/guides/gs/acce ...

  5. 【Spring】Spring Data JPA

    原始JDBC操作数据库 传统JDBC方式实现数据库操作 package com.imooc.util; import java.io.InputStream; import java.sql.*; i ...

  6. 实例对比 hibernate, spring data jpa, mybatis 选型参考

    原文: 最近重构以前写的服务,最大的一个变动是将mybatis切换为spring data jpa,切换的原因很简单,有两点:第一.它是spring的子项目能够和spring boot很好的融合,没有 ...

  7. 解决neo4j @Transactional 与Spring data jpa @Transactional 冲突问题,@CreatedBy,@CreatedDate,@LastModifiedBy,@LastModifiedDate,以及解决@Version失效问题

    之前mybatis特别流行,所以前几个项目都是用@SelectProvider,@InsertProvider,@UpdateProvider,@DeleteProvider 加反射泛型封装了一些通用 ...

  8. Spring Data JPA教程, 第三部分: Custom Queries with Query Methods(翻译)

    在本人的Spring Data JPA教程的第二部分描述了如何用Spring Data JPA创建一个简单的CRUD应用,本博文将描述如何在Spring Data JPA中使用query方法创建自定义 ...

  9. Spring boot 中Spring data JPA的应用(一)

    最近一直在研究Spring Boot,今天为大家介绍下Spring Data JPA在Spring Boot中的应用,如有错误,欢迎大家指正. 先解释下什么是JPA JPA就是一个基于O/R映射的标准 ...

随机推荐

  1. java.sql.SQLException: Operation not allowed after ResultSet closed

    转自:http://blog.csdn.net/hellobobantang/article/details/7173622 java.sql.SQLException: Operation not ...

  2. [转载]IOCP模型的总结

    原文:IOCP模型的总结 IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型.它是应用程序使用线程池处理异步I/O请求的一种机制.在处理多个并发的异步I/O请 ...

  3. window系统的HOST详解

    很多用户都知道在Window系统中有个Hosts文件(没有后缀名),在Windows 98系统下该文件在Windows目录,在Windows 2000/XP系统中位于C\Winnt\System32 ...

  4. 无废话WCF系列教程 -- 李林峰

    李林峰的无废话WCF入门教程 无废话WCF入门教程一[什么是WCF] 无废话WCF入门教程二[WCF应用的通信过程] 无废话WCF入门教程三[WCF的宿主] 无废话WCF入门教程四[WCF的配置文件] ...

  5. C#中使用GetCursorPos获取屏幕坐标

    [StructLayout(LayoutKind.Sequential)] public struct POINT { public int X; public int Y; public POINT ...

  6. 微信开放平台开发-授权、全网发布(PHP)

    这两天做了微信开发平台的开发,梳理下... 浙江百牛信息技术bainiu.ltd整理发布于博客园 先看看授权的流程: 第一步:接收component_verify_ticket: 1.微信服务器每隔1 ...

  7. Go 方法和接口

    转自:http://studygolang.com/topics/549 Go 没有类,但可以在结构体类型上定义方法. package main import ( "fmt" &q ...

  8. Elasticsearch学习概念之curl

    curl,简单认为是可以在命令行下访问url的一个工具.即增删改查. curl是利用URL语法在命令行方式下工作的开源文件传输工具,使用curl可以简单实现常见的get/post请求. 查看curl的 ...

  9. 要把target下面虚拟路径的项目文件…

     源码进不去,要检查target下面的项目文件,要删除掉. 版权声明:本文为博主原创文章,未经博主允许不得转载.

  10. shell程序---编译目录下全部.c或.cpp文件

    今天大波又提起昨天我说的那个程序.这样的,起初我想写一个makefile,每次写完新代码后一键编译目录下所有的.cpp文件. 原因是用makefile的话,每次要把目标文件加紧去才能编译.感觉不方便. ...